The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: c3a48b2342b9c7f7c9ae703f60904b63ab6d62ce $
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  */
25 RCSID("$Id: c3a48b2342b9c7f7c9ae703f60904b63ab6d62ce $")
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 
34 typedef 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.
38 } rlm_delay_t;
39 
40 /*
41  * A mapping of configuration file names to internal variables.
42  */
43 static const conf_parser_t module_config[] = {
44  { FR_CONF_OFFSET("delay", rlm_delay_t, delay) },
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 
50 typedef struct {
54 
55 /** Called when the timeout has expired
56  *
57  * Marks the request as resumable, and prints the delayed delay time.
58  */
59 static 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 
72 static 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 
87 static 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  */
121 static 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 
134 static 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 
196  fr_dcursor_insert(out, vb);
197 
198  return XLAT_ACTION_DONE;
199 }
200 
201 static 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 
253 yield:
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 
261  return unlang_xlat_yield(request, xlat_delay_resume, xlat_delay_cancel, ~FR_SIGNAL_CANCEL, yielded_at);
262 }
263 
264 static 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 
273 extern module_rlm_t rlm_delay;
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:481
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#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:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
#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
static fr_slen_t in
Definition: dict.h:821
#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:1359
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1403
#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.
Definition: merged_model.c:113
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
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
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:1079
fr_signal_t
Definition: signal.h:48
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
RETURN_MODULE_FAIL
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:561
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:140
bool single
Argument must only contain a single box.
Definition: xlat.h:148
#define XLAT_ARG_PARSER_TERMINATOR
Definition: xlat.h:166
xlat_action_t
Definition: xlat.h:35
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition: xlat.h:42
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition: xlat.h:41
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: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