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: 2b2f7db3022541835e4bae7bf928dbcadffa45d7 $
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: 2b2f7db3022541835e4bae7bf928dbcadffa45d7 $")
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/server/map_proc.h>
31#include <freeradius-devel/util/time.h>
32#include <freeradius-devel/unlang/xlat_func.h>
33
34typedef struct {
35 tmpl_t *delay; //!< How long we delay for.
36 bool relative; //!< Whether the delay is relative to the start of request processing.
37 bool force_reschedule; //!< Whether we should force rescheduling of the request.
39
40/*
41 * A mapping of configuration file names to internal variables.
42 */
43static const conf_parser_t module_config[] = {
45 { FR_CONF_OFFSET("relative", rlm_delay_t, relative), .dflt = "no" },
46 { FR_CONF_OFFSET("force_reschedule", rlm_delay_t, force_reschedule), .dflt = "no" },
48};
49
54
55/** Called when the timeout has expired
56 *
57 * Marks the request as resumable, and prints the delayed delay time.
58 */
59static void _delay_done(module_ctx_t const *mctx, request_t *request, fr_retry_t const *retry)
60{
61 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
62
63 RDEBUG2("Delay done");
64
65 /*
66 * timeout should never be *before* the scheduled time,
67 * if it is, something is very broken.
68 */
69 if (!fr_cond_assert(fr_time_gteq(retry->updated, yielded->when))) REDEBUG("Unexpected resume time");
70}
71
72static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
73{
74 fr_time_t *yielded = talloc_get_type_abort(xctx->rctx, fr_time_t);
75
76 RDEBUG2("Delay done");
77
78 /*
79 * timeout should never be *before* the scheduled time,
80 * if it is, something is very broken.
81 */
82 if (!fr_cond_assert(fr_time_gt(fired, *yielded))) REDEBUG("Unexpected resume time");
83
85}
86
87static int delay_add(request_t *request, fr_time_t *resume_at, fr_time_t now,
88 fr_time_delta_t delay, bool force_reschedule, bool relative)
89{
90 /*
91 * Delay is zero (and reschedule is not forced)
92 */
93 if (!force_reschedule && !fr_time_delta_ispos(delay)) return 1;
94
95 /*
96 * Process the delay relative to the start of packet processing
97 */
98 if (relative) {
99 *resume_at = fr_time_add(request->packet->timestamp, delay);
100 } else {
101 *resume_at = fr_time_add(now, delay);
102 }
103
104 /*
105 * If resume_at is in the past (and reschedule is not forced), just return noop
106 */
107 if (!force_reschedule && fr_time_lteq(*resume_at, now)) return 1;
108
109 if (fr_time_gt(*resume_at, now)) {
110 RDEBUG2("Delaying request by ~%pVs", fr_box_time_delta(fr_time_sub(*resume_at, now)));
111 } else {
112 RDEBUG2("Rescheduling request");
113 }
114
115 return 0;
116}
117
118/** Called resume_at the delay is complete, and we're running from the interpreter
119 *
120 */
121static unlang_action_t mod_delay_return(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
122{
123 rlm_delay_retry_t *yielded = talloc_get_type_abort(mctx->rctx, rlm_delay_retry_t);
124
125 /*
126 * Print how long the delay *really* was.
127 */
128 RDEBUG3("Request delayed by %pV", fr_box_time_delta(fr_time_sub(fr_time(), yielded->when)));
129 talloc_free(yielded);
130
132}
133
134static unlang_action_t CC_HINT(nonnull) mod_delay(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
135{
137 fr_time_delta_t delay;
138 rlm_delay_retry_t *yielded;
139 fr_time_t resume_at;
140
141 if (inst->delay) {
142 if (tmpl_aexpand_type(request, &delay, FR_TYPE_TIME_DELTA,
143 request, inst->delay, NULL, NULL) < 0) {
144 RPEDEBUG("Failed parsing %s as delay time", inst->delay->name);
146 }
147 } else {
148 delay = fr_time_delta_wrap(0);
149 }
150
151 /*
152 * Record the time that we yielded the request
153 */
154 MEM(yielded = talloc(unlang_interpret_frame_talloc_ctx(request), rlm_delay_retry_t));
155 yielded->when = fr_time();
156
157 /*
158 * Setup the delay for this request
159 */
160 if (delay_add(request, &resume_at, yielded->when, delay,
161 inst->force_reschedule, inst->relative) != 0) {
163 }
164
165 RDEBUG3("Current time %pVs, resume time %pVs",
166 fr_box_time(yielded->when), fr_box_time(resume_at));
167
168 /*
169 *
170 */
171 yielded->retry_cfg = (fr_retry_config_t) {
172 .mrd = delay,
173 .mrc = 1,
174 };
175
177 yielded, &yielded->retry_cfg);
178}
179
181 xlat_ctx_t const *xctx,
182 request_t *request, UNUSED fr_value_box_list_t *in)
183{
184 fr_time_t *yielded_at = talloc_get_type_abort(xctx->rctx, fr_time_t);
185 fr_time_delta_t delayed;
186 fr_value_box_t *vb;
187
188 delayed = fr_time_sub(fr_time(), *yielded_at);
189 talloc_free(yielded_at);
190
191 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_TIME_DELTA, NULL));
192 vb->vb_time_delta = delayed;
193
194 RDEBUG3("Request delayed by %pVs", vb);
195
197
198 return XLAT_ACTION_DONE;
199}
200
201static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
202{
203 RDEBUG2("Cancelling delay");
204}
205
207 { .single = true, .type = FR_TYPE_TIME_DELTA },
209};
210
211/** Xlat to delay the request
212 *
213 * Example (delay 2 seconds):
214@verbatim
215%delay(2)
216@endverbatim
217 *
218 * @ingroup xlat_functions
219 */
221 xlat_ctx_t const *xctx,
222 request_t *request, fr_value_box_list_t *in)
223{
224 rlm_delay_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_delay_t);
225 fr_time_t resume_at, *yielded_at;
226 fr_value_box_t *delay = fr_value_box_list_head(in);
227
228 /*
229 * Record the time that we yielded the request
230 */
231 MEM(yielded_at = talloc(request, fr_time_t));
232 *yielded_at = fr_time();
233
234 /*
235 * If there's no input delay, just yield and
236 * immediately re-enqueue the request.
237 * This is very useful for testing.
238 */
239 if (!delay) {
240 if (!fr_cond_assert(delay_add(request, &resume_at, *yielded_at, fr_time_delta_wrap(0), true, true) == 0)) {
241 return XLAT_ACTION_FAIL;
242 }
243 goto yield;
244 }
245
246 if (delay_add(request, &resume_at, *yielded_at, delay->vb_time_delta,
247 inst->force_reschedule, inst->relative) != 0) {
248 RDEBUG2("Not adding delay");
249 talloc_free(yielded_at);
250 return XLAT_ACTION_DONE;
251 }
252
253yield:
254 RDEBUG3("Current time %pVs, resume time %pVs", fr_box_time(*yielded_at), fr_box_time(resume_at));
255
256 if (unlang_xlat_timeout_add(request, _xlat_delay_done, yielded_at, resume_at) < 0) {
257 RPEDEBUG("Adding event failed");
258 return XLAT_ACTION_FAIL;
259 }
260
262}
263
264static int mod_bootstrap(module_inst_ctx_t const *mctx)
265{
266 xlat_t *xlat;
267
268 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, NULL, xlat_delay, FR_TYPE_TIME_DELTA);
270 return 0;
271}
272
275 .common = {
276 .magic = MODULE_MAGIC_INIT,
277 .name = "delay",
278 .flags = 0,
279 .inst_size = sizeof(rlm_delay_t),
281 .bootstrap = mod_bootstrap
282 },
283 .method_group = {
284 .bindings = (module_method_binding_t[]){
285 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_delay },
287 }
288 }
289};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
#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:435
#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:823
#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:220
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1363
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1407
#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:257
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:183
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RETURN_MODULE_NOOP
Definition rcode.h:62
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
tmpl_t * delay
How long we delay for.
Definition rlm_delay.c:35
static unlang_action_t mod_delay_return(rlm_rcode_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:121
bool relative
Whether the delay is relative to the start of request processing.
Definition rlm_delay.c:36
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:87
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_delay.c:264
fr_retry_config_t retry_cfg
Definition rlm_delay.c:51
bool force_reschedule
Whether we should force rescheduling of the request.
Definition rlm_delay.c:37
static void _xlat_delay_done(xlat_ctx_t const *xctx, request_t *request, fr_time_t fired)
Definition rlm_delay.c:72
module_rlm_t rlm_delay
Definition rlm_delay.c:274
static void xlat_delay_cancel(UNUSED xlat_ctx_t const *xctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_delay.c:201
static unlang_action_t mod_delay(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_delay.c:134
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:180
static xlat_arg_parser_t const xlat_delay_args[]
Definition rlm_delay.c:206
fr_time_t when
Definition rlm_delay.c:52
static const conf_parser_t module_config[]
Definition rlm_delay.c:43
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:59
#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:227
void * data
Module's instance data.
Definition module.h:271
void * boot
Data allocated during the boostrap phase.
Definition module.h:274
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
Named methods exported by a module.
Definition module.h:173
#define tmpl_aexpand_type(_ctx, _out, _type, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1085
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:361
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:282
#define fr_time_gteq(_a, _b)
Definition time.h:238
#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:573
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:152
bool single
Argument must only contain a single box.
Definition xlat.h:150
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:168
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:147
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:621
#define fr_box_time_delta(_val)
Definition value.h:343
int nonnull(2, 5))
#define fr_box_time(_val)
Definition value.h:326
static size_t char ** out
Definition value.h:997
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:365