The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sqlcounter.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: 786dbc7b275693a65ad9a963c3347f10cae8c73b $
19 * @file rlm_sqlcounter.c
20 * @brief Tracks data usage and other counters using SQL.
21 *
22 * @copyright 2001,2006 The FreeRADIUS server project
23 * @copyright 2001 Alan DeKok (aland@freeradius.org)
24 */
25RCSID("$Id: 786dbc7b275693a65ad9a963c3347f10cae8c73b $")
26
27#define LOG_PREFIX "sqlcounter"
28
29#include <rlm_sql.h>
30#include <freeradius-devel/server/base.h>
31#include <freeradius-devel/server/module_rlm.h>
32#include <freeradius-devel/util/debug.h>
33#include <freeradius-devel/unlang/function.h>
34
35/*
36 * Note: When your counter spans more than 1 period (ie 3 months
37 * or 2 weeks), this module probably does NOT do what you want! It
38 * calculates the range of dates to count across by first calculating
39 * the End of the Current period and then subtracting the number of
40 * periods you specify from that to determine the beginning of the
41 * range.
42 *
43 * For example, if you specify a 3 month counter and today is June 15th,
44 * the end of the current period is June 30. Subtracting 3 months from
45 * that gives April 1st. So, the counter will sum radacct entries from
46 * April 1st to June 30. Then, next month, it will sum entries from
47 * May 1st to July 31st.
48 *
49 * To fix this behavior, we need to add some way of storing the Next
50 * Reset Time.
51 */
52
53/*
54 * Define a structure for our module configuration.
55 *
56 * These variables do not need to be in a structure, but it's
57 * a lot cleaner to do so, and a pointer to the structure can
58 * be used as the instance handle.
59 */
60typedef struct {
61 tmpl_t *start_attr; //!< control.${.:instance}-Start
62 tmpl_t *end_attr; //!< control.${.:instance}-End
63
64 tmpl_t *counter_attr; //!< Daily-Session-Time.
65 tmpl_t *limit_attr; //!< Max-Daily-Session.
66 tmpl_t *key; //!< User-Name
67
68 char const *sql_name; //!< Instance of SQL module to use, usually just 'sql'.
69 char const *query; //!< SQL query to retrieve current session time.
70 char const *reset; //!< Daily, weekly, monthly, never or user defined.
71 bool auto_extend; //!< If the remaining allowance is sufficient to reach the next
72 ///< period allow for that in setting the reply attribute.
73 bool utc; //!< Use UTC time.
74
78
79static const conf_parser_t module_config[] = {
80 { FR_CONF_OFFSET_FLAGS("sql_module_instance", CONF_FLAG_REQUIRED, rlm_sqlcounter_t, sql_name) },
81
82
85 { FR_CONF_OFFSET_FLAGS("auto_extend", CONF_FLAG_OK_MISSING, rlm_sqlcounter_t, auto_extend) },
87
88 { FR_CONF_OFFSET_FLAGS("key", CONF_FLAG_NOT_EMPTY, rlm_sqlcounter_t, key), .dflt = "%{Stripped-User-Name || User-Name}", .quote = T_DOUBLE_QUOTED_STRING },
89
90 { FR_CONF_OFFSET_FLAGS("reset_period_start_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, start_attr),
91 .dflt = "control.${.:instance}-Reset-Start", .quote = T_BARE_WORD },
92 { FR_CONF_OFFSET_FLAGS("reset_period_end_name", CONF_FLAG_ATTRIBUTE, rlm_sqlcounter_t, end_attr),
93 .dflt = "control.${.:instance}-Reset-End", .quote = T_BARE_WORD },
94
95 /* Attribute to write counter value to*/
98
100};
101
102typedef struct {
103 xlat_exp_head_t *query_xlat; //!< Tokenized xlat to run query.
104 tmpl_t *reply_attr; //!< Attribute to write timeout to.
105 tmpl_t *reply_msg_attr; //!< Attribute to write reply message to.
107
109
112 { .out = &dict_freeradius, .proto = "freeradius" },
114};
115
117{
118 int ret = 0;
119 size_t len;
120 unsigned int num = 1;
121 char last = '\0';
122 struct tm *tm, s_tm;
123 time_t time_s = fr_time_to_sec(now);
124
125 if (inst->utc) {
126 tm = gmtime_r(&time_s, &s_tm);
127 } else {
128 tm = localtime_r(&time_s, &s_tm);
129 }
130 tm->tm_sec = tm->tm_min = 0;
131
132 fr_assert(inst->reset != NULL);
133
134 if (isdigit((uint8_t) inst->reset[0])){
135 len = strlen(inst->reset);
136 if (len == 0)
137 return -1;
138 last = inst->reset[len - 1];
139 if (!isalpha((uint8_t) last))
140 last = 'd';
141 num = atoi(inst->reset);
142 DEBUG3("num=%d, last=%c",num,last);
143 }
144 if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
145 /*
146 * Round up to the next nearest hour.
147 */
148 tm->tm_hour += num;
149 inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
150 } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
151 /*
152 * Round up to the next nearest day.
153 */
154 tm->tm_hour = 0;
155 tm->tm_mday += num;
156 inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
157 } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
158 /*
159 * Round up to the next nearest week.
160 */
161 tm->tm_hour = 0;
162 tm->tm_mday += (7 - tm->tm_wday) +(7*(num-1));
163 inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
164 } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
165 tm->tm_hour = 0;
166 tm->tm_mday = 1;
167 tm->tm_mon += num;
168 inst->reset_time = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
169 } else if (strcmp(inst->reset, "never") == 0) {
170 inst->reset_time = fr_time_wrap(0);
171 } else {
172 return -1;
173 }
174
175 DEBUG2("Current Time: %pV, Next reset %pV", fr_box_time(now), fr_box_time(inst->reset_time));
176
177 return ret;
178}
179
180
181/* I don't believe that this routine handles Daylight Saving Time adjustments
182 properly. Any suggestions?
183*/
185{
186 int ret = 0;
187 size_t len;
188 unsigned int num = 1;
189 char last = '\0';
190 struct tm *tm, s_tm;
191 time_t time_s = fr_time_to_sec(now);
192
193 if (inst->utc) {
194 tm = gmtime_r(&time_s, &s_tm);
195 } else {
196 tm = localtime_r(&time_s, &s_tm);
197 }
198 tm->tm_sec = tm->tm_min = 0;
199
200 fr_assert(inst->reset != NULL);
201
202 if (isdigit((uint8_t) inst->reset[0])){
203 len = strlen(inst->reset);
204 if (len == 0)
205 return -1;
206 last = inst->reset[len - 1];
207 if (!isalpha((uint8_t) last))
208 last = 'd';
209 num = atoi(inst->reset);
210 DEBUG3("num=%d, last=%c", num, last);
211 }
212 if (strcmp(inst->reset, "hourly") == 0 || last == 'h') {
213 /*
214 * Round down to the prev nearest hour.
215 */
216 tm->tm_hour -= num - 1;
217 inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
218 } else if (strcmp(inst->reset, "daily") == 0 || last == 'd') {
219 /*
220 * Round down to the prev nearest day.
221 */
222 tm->tm_hour = 0;
223 tm->tm_mday -= num - 1;
224 inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
225 } else if (strcmp(inst->reset, "weekly") == 0 || last == 'w') {
226 /*
227 * Round down to the prev nearest week.
228 */
229 tm->tm_hour = 0;
230 tm->tm_mday -= tm->tm_wday +(7*(num-1));
231 inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
232 } else if (strcmp(inst->reset, "monthly") == 0 || last == 'm') {
233 tm->tm_hour = 0;
234 tm->tm_mday = 1;
235 tm->tm_mon -= num - 1;
236 inst->last_reset = fr_time_from_sec(inst->utc ? timegm(tm) : mktime(tm));
237 } else if (strcmp(inst->reset, "never") == 0) {
238 inst->last_reset = fr_time_wrap(0);
239 } else {
240 return -1;
241 }
242
243 DEBUG2("Current Time: %pV, Prev reset %pV", fr_box_time(now), fr_box_time(inst->last_reset));
244
245 return ret;
246}
247
255
256/** Handle the result of calling the SQL query to retrieve the `counter` value.
257 *
258 * Create / update the `counter` attribute in the control list
259 * If `counter` > `limit`, optionally populate a reply message and return RLM_MODULE_REJECT.
260 * Otherwise, optionally populate a reply attribute with the value of `limit` - `counter` and return RLM_MODULE_UPDATED.
261 * If no reply attribute is set, return RLM_MODULE_OK.
262 */
264{
265 sqlcounter_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, sqlcounter_rctx_t);
266 rlm_sqlcounter_t *inst = rctx->inst;
267 sqlcounter_call_env_t *env = rctx->env;
268 fr_value_box_t *sql_result = fr_value_box_list_pop_head(&rctx->result);
269 uint64_t counter, res;
270 fr_pair_t *vp, *limit = rctx->limit;
271 int ret;
272 char msg[128];
273
274 if (!sql_result || (sscanf(sql_result->vb_strvalue, "%" PRIu64, &counter) != 1)) {
275 RDEBUG2("No integer found in result string \"%pV\". May be first session, setting counter to 0",
276 sql_result);
277 counter = 0;
278 }
279
280 /*
281 * Add the counter to the control list
282 */
283 MEM(pair_update_control(&vp, tmpl_attr_tail_da(inst->counter_attr)) >= 0);
284 vp->vp_uint64 = counter;
285
286 /*
287 * Check if check item > counter
288 */
289 if (limit->vp_uint64 <= counter) {
290 if (env->reply_msg_attr) {
291 /* User is denied access, send back a reply message */
292 snprintf(msg, sizeof(msg), "Your maximum %s usage has been reached", inst->reset);
293
295 fr_pair_value_strdup(vp, msg, false);
296 }
297
298 REDEBUG2("Maximum %s usage reached", inst->reset);
299 REDEBUG2("Rejecting user, %s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
300 inst->limit_attr->name, limit->vp_uint64, counter);
301
303 }
304
305 res = limit->vp_uint64 - counter;
306 RDEBUG2("Allowing user, %s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")",
307 inst->limit_attr->name, limit->vp_uint64, counter);
308
309 /*
310 * We are assuming that simultaneous-use=1. But
311 * even if that does not happen then our user
312 * could login at max for 2*max-usage-time Is
313 * that acceptable?
314 */
315 if (env->reply_attr) {
317
318 /*
319 * If we are near a reset then add the next
320 * limit, so that the user will not need to login
321 * again. Do this only if auto_extend is set.
322 */
323 if (inst->auto_extend &&
324 fr_time_gt(inst->reset_time, fr_time_wrap(0)) &&
325 ((int64_t)res >= fr_time_delta_to_sec(fr_time_sub(inst->reset_time, request->packet->timestamp)))) {
326 fr_time_delta_t to_reset = fr_time_sub(inst->reset_time, request->packet->timestamp);
327
328 RDEBUG2("Time remaining (%pV) is greater than time to reset (%" PRIu64 "s). "
329 "Adding %pV to reply value",
330 fr_box_time_delta(to_reset), res, fr_box_time_delta(to_reset));
331 res = fr_time_delta_to_sec(to_reset) + limit->vp_uint64;
332 }
333
334 fr_value_box_init(&vb, FR_TYPE_UINT64, NULL, false);
335 vb.vb_uint64 = res;
336
337 /*
338 * Limit the reply attribute to the minimum of the existing value, or this new one.
339 */
340 ret = tmpl_find_or_add_vp(&vp, request, env->reply_attr);
341 switch (ret) {
342 case 1: /* new */
343 break;
344
345 case 0: /* found */
346 {
347 fr_value_box_t existing;
348 if (fr_value_box_cast(NULL, &existing, FR_TYPE_UINT64, NULL, &vp->data) < 0) {
349 RPEDEBUG("Failed casting to uint64");
351 }
352 if (fr_value_box_cmp(&vb, &existing) == 1) {
353 RDEBUG2("Leaving existing %s value of %pV" , env->reply_attr->name,
354 &vp->data);
356 }
357 }
358 break;
359
360 case -1: /* alloc failed */
361 REDEBUG("Error allocating attribute %s", env->reply_attr->name);
363
364 default: /* request or list unavailable */
365 RDEBUG2("List or request context not available for %s, skipping...", env->reply_attr->name);
367 }
368
369 if (fr_value_box_cast(vp, &vp->data, vp->data.type, NULL, &vb) < 0) {
370 RPEDEBUG("Failed casting to %s", fr_type_to_str(vp->data.type));
372 }
373
374 RDEBUG2("%pP", vp);
375
377 }
378
380}
381
382/** Check the value of a `counter` retrieved from an SQL query with a `limit`
383 *
384 * Module specific attributes containing the start / end times are created / updated,
385 * the query is tokenized as an xlat call to the relevant SQL module and then
386 * pushed on the stack for evaluation.
387 */
388static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
389{
390 rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
391 sqlcounter_call_env_t *env = talloc_get_type_abort(mctx->env_data, sqlcounter_call_env_t);
392 fr_pair_t *limit, *vp;
393 sqlcounter_rctx_t *rctx;
394
395 /*
396 * Before doing anything else, see if we have to reset
397 * the counters.
398 */
399 if (fr_time_neq(inst->reset_time, fr_time_wrap(0)) &&
400 (fr_time_lteq(inst->reset_time, request->packet->timestamp))) {
401 /*
402 * Re-set the next time and prev_time for this counters range
403 */
404 inst->last_reset = inst->reset_time;
405 find_next_reset(inst, request->packet->timestamp);
406 }
407
408 if (tmpl_find_vp(&limit, request, inst->limit_attr) < 0) {
409 RWDEBUG2("Couldn't find %s, doing nothing...", inst->limit_attr->name);
411 }
412
413 /*
414 * Populate start and end attributes for use in query expansion
415 */
416 if (tmpl_find_or_add_vp(&vp, request, inst->start_attr) < 0) {
417 REDEBUG("Couldn't create %s", inst->start_attr->name);
419 }
420 vp->vp_uint64 = fr_time_to_sec(inst->last_reset);
421
422 if (tmpl_find_or_add_vp(&vp, request, inst->end_attr) < 0) {
423 REDEBUG2("Couldn't create %s", inst->end_attr->name);
425 }
426 vp->vp_uint64 = fr_time_to_sec(inst->reset_time);
427
429 *rctx = (sqlcounter_rctx_t) {
430 .inst = inst,
431 .env = env,
432 .limit = limit
433 };
434
435 if (unlang_module_yield(request, mod_authorize_resume, NULL, 0, rctx) == UNLANG_ACTION_FAIL) {
436 error:
437 talloc_free(rctx);
439 }
440
441 fr_value_box_list_init(&rctx->result);
442 if (unlang_xlat_push(rctx, &rctx->last_result, &rctx->result, request, env->query_xlat, UNLANG_SUB_FRAME) < 0) goto error;
443
445}
446
447/** Custom call_env parser to tokenize the SQL query xlat used for counter retrieval
448 */
449static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
450 call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
451{
453 CONF_PAIR const *to_parse = cf_item_to_pair(ci);
454 char *query;
455 xlat_exp_head_t *ex;
456
457 query = talloc_asprintf(NULL, "%%%s(\"%s\")", inst->sql_name, cf_pair_value(to_parse));
458
459 if (xlat_tokenize(ctx, &ex,
460 &FR_SBUFF_IN(query, talloc_array_length(query)),
461 &(fr_sbuff_parse_rules_t){
462 .escapes = &(fr_sbuff_unescape_rules_t) {
463 .name = "xlat",
464 .chr = '\\',
465 .subs = {
466 ['%'] = '%',
467 ['\\'] = '\\',
468 },
469 }}, t_rules) < 0) {
470 talloc_free(query);
471 return -1;
472 }
473 talloc_free(query);
474
475 if (xlat_needs_resolving(ex) &&
476 (xlat_resolve(ex, &(xlat_res_rules_t){ .allow_unresolved = false }) < 0)) {
477 talloc_free(ex);
478 return -1;
479 }
480
481 *(void**)out = ex;
482 return 0;
483}
484
495
496
497/*
498 * Do any per-module initialization that is separate to each
499 * configured instance of the module. e.g. set up connections
500 * to external databases, read configuration files, set up
501 * dictionary entries, etc.
502 *
503 * If configuration information is given in the config section
504 * that must be referenced in later calls, store a handle to it
505 * in *instance otherwise put a null pointer there.
506 */
507static int mod_instantiate(module_inst_ctx_t const *mctx)
508{
509 rlm_sqlcounter_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
510 CONF_SECTION *conf = mctx->mi->conf;
512 fr_assert(inst->query && *inst->query);
513
514 sql_inst = module_rlm_static_by_name(NULL, inst->sql_name);
515 if (!sql_inst) {
516 cf_log_err(conf, "Module \"%s\" not found", inst->sql_name);
517 return -1;
518 }
519
520 if (!talloc_get_type(sql_inst->data, rlm_sql_t)) {
521 cf_log_err(conf, "\"%s\" is not an instance of rlm_sql", inst->sql_name);
522 return -1;
523 }
524
525 inst->reset_time = fr_time_wrap(0);
526
527 if (find_next_reset(inst, fr_time()) == -1) {
528 cf_log_err(conf, "Invalid reset '%s'", inst->reset);
529 return -1;
530 }
531
532 /*
533 * Discover the beginning of the current time period.
534 */
535 inst->last_reset = fr_time_wrap(0);
536
537 if (find_prev_reset(inst, fr_time()) < 0) {
538 cf_log_err(conf, "Invalid reset '%s'", inst->reset);
539 return -1;
540 }
541
542 return 0;
543}
544
545static inline int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
546{
549 tmpl_attr_tail_unresolved(tmpl), FR_TYPE_UINT64, flags) < 0) {
550 cf_log_perr(conf, "Failed defining %s attribute", name);
551 return -1;
552 }
553 } else if (tmpl_is_attr(tmpl)) {
554 if (tmpl_attr_tail_da(tmpl)->type != FR_TYPE_UINT64) {
555 cf_log_err(conf, "%s attribute %s must be uint64", name, tmpl_attr_tail_da(tmpl)->name);
556 return -1;
557 }
558 }
559
560 return 0;
561}
562
563static int mod_bootstrap(module_inst_ctx_t const *mctx)
564{
565 rlm_sqlcounter_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_sqlcounter_t);
566 CONF_SECTION *conf = mctx->mi->conf;
567 fr_dict_attr_flags_t flags = { .internal = 1, .length = 8, .name_only = 1 };
568
569 if (unlikely(attr_check(conf, inst->start_attr, "reset_period_start", &flags) < 0)) return -1;
570 if (unlikely(attr_check(conf, inst->end_attr, "reset_period_end", &flags) < 0)) return -1;
571 if (unlikely(attr_check(conf, inst->counter_attr, "counter", &flags) < 0)) return -1;
572 if (unlikely(attr_check(conf, inst->limit_attr, "check", &flags) < 0)) return -1;
573
574 return 0;
575}
576
577/*
578 * The module name should be the only globally exported symbol.
579 * That is, everything else should be 'static'.
580 *
581 * If the module needs to temporarily modify it's instantiation
582 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
583 * The server will then take care of ensuring that the module
584 * is single-threaded.
585 */
588 .common = {
589 .magic = MODULE_MAGIC_INIT,
590 .name = "sqlcounter",
591 .inst_size = sizeof(rlm_sqlcounter_t),
593 .bootstrap = mod_bootstrap,
594 .instantiate = mod_instantiate,
595 },
596 .method_group = {
597 .bindings = (module_method_binding_t[]){
598 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_authorize, .method_env = &sqlcounter_call_env },
600 }
601 }
602};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
log_entry msg
Definition acutest.h:794
#define RCSID(id)
Definition build.h:488
#define unlikely(_x)
Definition build.h:384
#define UNUSED
Definition build.h:318
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition call_env.h:85
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
module_instance_t const * mi
Module instance that the callenv is registered to.
Definition call_env.h:229
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition call_env.h:389
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
fr_token_t quote
Quoting around the default value. Only used for templates.
Definition cf_parse.h:649
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _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
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:429
@ CONF_FLAG_ATTRIBUTE
Value must resolve to attribute in dict (deprecated, use CONF_FLAG_TMPL).
Definition cf_parse.h:431
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:447
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition cf_parse.h:443
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition cf_parse.h:454
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:72
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1574
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:292
#define CF_IDENT_ANY
Definition cf_util.h:75
#define MEM(x)
Definition debug.h:46
int fr_dict_attr_add_name_only(fr_dict_t *dict, fr_dict_attr_t const *parent, char const *name, fr_type_t type, fr_dict_attr_flags_t const *flags))
Add an attribute to the dictionary.
Definition dict_util.c:2006
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4903
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3528
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2665
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition dict.h:88
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Values of the encryption flags.
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
talloc_free(hp)
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 UNLANG_SUB_FRAME
Definition interpret.h:37
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RWDEBUG2(fmt,...)
Definition log.h:374
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define REDEBUG2(fmt,...)
Definition log.h:384
#define fr_time()
Definition event.c:60
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
unsigned char uint8_t
struct tm * gmtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:205
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:162
void * env_data
Per call environment data.
Definition module_ctx.h:44
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
module_instance_t * module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name)
Definition module_rlm.c:825
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2667
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
#define RETURN_UNLANG_UPDATED
Definition rcode.h:70
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
static char const * name
Prototypes and functions for the SQL module.
static int attr_check(CONF_SECTION *conf, tmpl_t *tmpl, char const *name, fr_dict_attr_flags_t *flags)
bool auto_extend
If the remaining allowance is sufficient to reach the next period allow for that in setting the reply...
module_rlm_t rlm_sqlcounter
char const * sql_name
Instance of SQL module to use, usually just 'sql'.
unlang_result_t last_result
char const * reset
Daily, weekly, monthly, never or user defined.
static unlang_action_t mod_authorize_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Handle the result of calling the SQL query to retrieve the counter value.
static fr_dict_t const * dict_freeradius
static int mod_bootstrap(module_inst_ctx_t const *mctx)
xlat_exp_head_t * query_xlat
Tokenized xlat to run query.
tmpl_t * start_attr
control.${.:instance}-Start
tmpl_t * reply_msg_attr
Attribute to write reply message to.
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Check the value of a counter retrieved from an SQL query with a limit
static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Custom call_env parser to tokenize the SQL query xlat used for counter retrieval.
tmpl_t * counter_attr
Daily-Session-Time.
sqlcounter_call_env_t * env
char const * query
SQL query to retrieve current session time.
fr_value_box_list_t result
tmpl_t * limit_attr
Max-Daily-Session.
tmpl_t * end_attr
control.${.:instance}-End
rlm_sqlcounter_t * inst
tmpl_t * reply_attr
Attribute to write timeout to.
static int find_next_reset(rlm_sqlcounter_t *inst, fr_time_t now)
static const call_env_method_t sqlcounter_call_env
bool utc
Use UTC time.
static const conf_parser_t module_config[]
fr_dict_autoload_t rlm_sqlcounter_dict[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
static int find_prev_reset(rlm_sqlcounter_t *inst, fr_time_t now)
tmpl_t * key
User-Name.
#define FR_SBUFF_IN(_start, _len_or_end)
Set of parsing rules for *unescape_until functions.
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Module instance data.
Definition module.h:285
Named methods exported by a module.
Definition module.h:174
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition pair.h:129
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition pair.h:140
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition tmpl_eval.c:776
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:219
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
static char const * tmpl_attr_tail_unresolved(tmpl_t const *vpt)
Return the last attribute reference unresolved da.
Definition tmpl.h:869
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
Returns the first VP matching a tmpl_t, or if no VPs match, creates a new one.
Definition tmpl_eval.c:805
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_asprintf
Definition talloc.h:144
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition time.h:731
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_lteq(_a, _b)
Definition time.h:240
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition time.h:858
#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
#define fr_time_neq(_a, _b)
Definition time.h:242
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:118
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
Definition xlat.c:269
bool xlat_needs_resolving(xlat_exp_head_t const *head)
Check to see if the expansion needs resolving.
fr_slen_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules)
Tokenize an xlat expansion.
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules)
Walk over an xlat tree recursively, resolving any unresolved functions or references.
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:454
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition value.c:3960
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:748
#define fr_box_time_delta(_val)
Definition value.h:366
int nonnull(2, 5))
#define fr_box_time(_val)
Definition value.h:349
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
static size_t char ** out
Definition value.h:1030