The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_rediswho.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: 1c8a9200957305b9d3be94ddf8a30077c6fad248 $
19  * @file rlm_rediswho.c
20  * @brief Session tracking using redis.
21  *
22  * @author Gabriel Blanchard
23  *
24  * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25  * @copyright 2011 TekSavvy Solutions (gabe@teksavvy.com)
26  * @copyright 2000,2006 The FreeRADIUS server project
27  */
28 
29 RCSID("$Id: 1c8a9200957305b9d3be94ddf8a30077c6fad248 $")
30 
31 #include <freeradius-devel/server/base.h>
32 #include <freeradius-devel/server/module_rlm.h>
33 #include <freeradius-devel/server/modpriv.h>
34 #include <freeradius-devel/util/debug.h>
35 
36 #include <freeradius-devel/redis/base.h>
37 #include <freeradius-devel/redis/cluster.h>
38 
39 typedef struct {
40  fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
41  //!< Must be first field in this struct.
42 
43  fr_redis_cluster_t *cluster; //!< Pool O pools
44 
45  int expiry_time; //!< Expiry time in seconds if no updates are received for a user
46 
47  int trim_count; //!< How many session updates to keep track of per user.
48 
49  char const *insert; //!< Command for inserting session data
50  char const *trim; //!< Command for trimming the session list.
51  char const *expire; //!< Command for expiring entries.
53 
56  { FR_CONF_OFFSET_FLAGS("trim", CONF_FLAG_XLAT, rlm_rediswho_t, trim) }, /* required only if trim_count > 0 */
59 };
60 
63 
64  { FR_CONF_OFFSET("trim_count", rlm_rediswho_t, trim_count), .dflt = "-1" },
65 
66  /*
67  * These all smash the same variables, because we don't care about them right now.
68  * In 3.1, we should have a way of saying "parse a set of sub-sections according to a template"
69  */
70  { FR_CONF_POINTER("Start", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
71  { FR_CONF_POINTER("Interim-Update", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
72  { FR_CONF_POINTER("Stop", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
73  { FR_CONF_POINTER("Accounting-On", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
74  { FR_CONF_POINTER("Accounting-Off", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
75  { FR_CONF_POINTER("Failed", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = section_config },
76 
78 };
79 
80 static fr_dict_t const *dict_radius;
81 
84  { .out = &dict_radius, .proto = "radius" },
85  { NULL }
86 };
87 
89 
92  { .out = &attr_acct_status_type, .name = "Acct-Status-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
93  { NULL }
94 };
95 
96 /*
97  * Query the database executing a command with no result rows
98  */
99 static int rediswho_command(rlm_rediswho_t const *inst, request_t *request, char const *fmt)
100 {
101  fr_redis_conn_t *conn;
102 
103  int ret = -1;
104 
106  fr_redis_rcode_t status;
107  redisReply *reply = NULL;
108  int s_ret;
109 
110  uint8_t const *key = NULL;
111  size_t key_len = 0;
112 
113  int argc;
114  char const *argv[MAX_REDIS_ARGS];
115  char argv_buf[MAX_REDIS_COMMAND_LEN];
116 
117  if (!fmt || !*fmt) return 0;
118 
119  argc = rad_expand_xlat(request, fmt, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
120  if (argc < 0) {
121  RPEDEBUG("Invalid command: %s", fmt);
122  return -1;
123  }
124 
125  /*
126  * If we've got multiple arguments, the second one is usually the key.
127  * The Redis docs say commands should be analysed first to get key
128  * positions, but this involves sending them to the server, which is
129  * just as expensive as sending them to the wrong server and receiving
130  * a redirect.
131  */
132  if (argc > 1) {
133  key = (uint8_t const *)argv[1];
134  key_len = strlen((char const *)key);
135  }
136 
137  for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, false);
138  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
139  s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
140  reply = redisCommandArgv(conn->handle, argc, argv, NULL);
141  status = fr_redis_command_status(conn, reply);
142  }
143  if (s_ret != REDIS_RCODE_SUCCESS) {
144  RERROR("Failed inserting accounting data");
145  error:
146  fr_redis_reply_free(&reply);
147  return -1;
148  }
149  if (!fr_cond_assert(reply)) goto error;
150 
151  /*
152  * Write the response to the debug log
153  */
154  fr_redis_reply_print(L_DBG_LVL_2, reply, request, 0);
155 
156  switch (reply->type) {
157  case REDIS_REPLY_ERROR:
158  break;
159 
160  case REDIS_REPLY_INTEGER:
161  if (reply->integer > 0) ret = reply->integer;
162  break;
163 
164  /*
165  * We don't know to interpret this, the user has probably messed
166  * up the queries, so print an error message and fail.
167  */
168  default:
169  REDEBUG("Expected type \"integer\" got type \"%s\"",
170  fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
171  break;
172  }
173  fr_redis_reply_free(&reply);
174 
175  return ret;
176 }
177 
179  char const *insert,
180  char const *trim,
181  char const *expire)
182 {
183  int ret;
184 
185  ret = rediswho_command(inst, request, insert);
186  if (ret < 0) RETURN_MODULE_FAIL;
187 
188  /* Only trim if necessary */
189  if ((inst->trim_count >= 0) && (ret > inst->trim_count)) {
190  if (rediswho_command(inst, request, trim) < 0) RETURN_MODULE_FAIL;
191  }
192 
193  if (rediswho_command(inst, request, expire) < 0) RETURN_MODULE_FAIL;
195 }
196 
197 static unlang_action_t CC_HINT(nonnull) mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
198 {
200  CONF_SECTION *conf = mctx->mi->conf;
201  rlm_rcode_t rcode;
202  fr_pair_t *vp;
204  CONF_SECTION *cs;
205  char const *insert, *trim, *expire;
206 
207  vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_acct_status_type);
208  if (!vp) {
209  RDEBUG2("Could not find account status type in packet");
211  }
212 
213  dv = fr_dict_enum_by_value(vp->da, &vp->data);
214  if (!dv) {
215  RDEBUG2("Unknown Acct-Status-Type %u", vp->vp_uint32);
217  }
218 
219  cs = cf_section_find(conf, dv->name, NULL);
220  if (!cs) {
221  RDEBUG2("No subsection %s", dv->name);
223  }
224 
225  insert = cf_pair_value(cf_pair_find(cs, "insert"));
226  trim = cf_pair_value(cf_pair_find(cs, "trim"));
227  expire = cf_pair_value(cf_pair_find(cs, "expire"));
228 
229  return mod_accounting_all(&rcode, inst, request, insert, trim, expire);
230 }
231 
232 static int mod_instantiate(module_inst_ctx_t const *mctx)
233 {
234  rlm_rediswho_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_rediswho_t);
235  CONF_SECTION *conf = mctx->mi->conf;
236 
237  inst->cluster = fr_redis_cluster_alloc(inst, conf, &inst->conf, true, NULL, NULL, NULL);
238  if (!inst->cluster) return -1;
239 
240  return 0;
241 }
242 
243 static int mod_load(void)
244 {
246 
247  return 0;
248 }
249 
252  .common = {
253  .magic = MODULE_MAGIC_INIT,
254  .name = "rediswho",
255  .inst_size = sizeof(rlm_rediswho_t),
257  .onload = mod_load,
259  },
260  .method_group = {
261  .bindings = (module_method_binding_t[]){
262  { .section = SECTION_NAME("accounting", CF_IDENT_ANY), .method = mod_accounting },
264  }
265  }
266 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char * fmt
Definition: acutest.h:573
#define RCSID(id)
Definition: build.h:481
#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
#define FR_CONF_POINTER(_name, _type, _flags, _res_p)
conf_parser_t which parses a single CONF_PAIR producing a single global result
Definition: cf_parse.h:310
#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:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: cf_parse.h:405
@ CONF_FLAG_XLAT
string will be dynamically expanded.
Definition: cf_parse.h:416
@ CONF_FLAG_SUBSECTION
Instead of putting the information into a configuration structure, the configuration file routines MA...
Definition: cf_parse.h:399
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition: cf_util.c:1028
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition: cf_util.c:1439
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1594
#define CF_IDENT_ANY
Definition: cf_util.h:78
fr_redis_rcode_t fr_redis_cluster_state_next(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
Definition: cluster.c:1863
fr_redis_rcode_t fr_redis_cluster_state_init(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len, bool read_only)
Resolve a key to a pool, and reserve a connection in that pool.
Definition: cluster.c:1741
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, bool triggers_enabled, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Allocate and initialise a new cluster structure.
Definition: cluster.c:2261
A redis cluster.
Definition: cluster.c:251
Redis connection sequence state.
Definition: cluster.h:49
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:139
fr_dict_enum_value_t * fr_dict_enum_by_value(fr_dict_attr_t const *da, fr_value_box_t const *value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition: dict_util.c:3349
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
char const * name
Enum name.
Definition: dict.h:227
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
Value of an enumerated attribute.
Definition: dict.h:226
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define RERROR(fmt,...)
Definition: log.h:298
#define RPEDEBUG(fmt,...)
Definition: log.h:376
int rad_expand_xlat(request_t *request, char const *cmd, int max_argc, char const *argv[], bool can_fail, size_t argv_buflen, char *argv_buf)
Split string into words and expand each one.
Definition: util.c:599
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition: log.h:71
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
unsigned char uint8_t
Definition: merged_model.c:30
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
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_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
static const conf_parser_t config[]
Definition: base.c:183
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
static rs_t * conf
Definition: radsniff.c:53
#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
#define MAX_REDIS_COMMAND_LEN
Definition: base.h:43
redisContext * handle
Hiredis context used when issuing commands.
Definition: base.h:101
#define REDIS_COMMON_CONFIG
Definition: base.h:133
#define MAX_REDIS_ARGS
Definition: base.h:44
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:141
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: base.h:64
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:53
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:71
fr_table_num_sorted_t const redis_reply_types[]
Definition: redis.c:30
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: base.h:87
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition: base.h:88
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Definition: base.h:90
Configuration parameters for a redis connection.
Definition: base.h:109
Connection handle, holding a redis context.
Definition: base.h:100
static int mod_load(void)
Definition: rlm_rediswho.c:243
fr_dict_autoload_t rlm_rediswho_dict[]
Definition: rlm_rediswho.c:83
fr_dict_attr_autoload_t rlm_rediswho_dict_attr[]
Definition: rlm_rediswho.c:91
int trim_count
How many session updates to keep track of per user.
Definition: rlm_rediswho.c:47
int expiry_time
Expiry time in seconds if no updates are received for a user.
Definition: rlm_rediswho.c:45
fr_redis_conf_t conf
Connection parameters for the Redis server.
Definition: rlm_rediswho.c:40
char const * insert
Command for inserting session data.
Definition: rlm_rediswho.c:49
static fr_dict_t const * dict_radius
Definition: rlm_rediswho.c:80
fr_redis_cluster_t * cluster
Pool O pools.
Definition: rlm_rediswho.c:43
module_rlm_t rlm_rediswho
Definition: rlm_rediswho.c:251
static unlang_action_t mod_accounting(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_rediswho.c:197
static conf_parser_t section_config[]
Definition: rlm_rediswho.c:54
static fr_dict_attr_t const * attr_acct_status_type
Definition: rlm_rediswho.c:88
static int rediswho_command(rlm_rediswho_t const *inst, request_t *request, char const *fmt)
Definition: rlm_rediswho.c:99
char const * expire
Command for expiring entries.
Definition: rlm_rediswho.c:51
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rediswho.c:232
char const * trim
Command for trimming the session list.
Definition: rlm_rediswho.c:50
static conf_parser_t module_config[]
Definition: rlm_rediswho.c:61
static unlang_action_t mod_accounting_all(rlm_rcode_t *p_result, rlm_rediswho_t const *inst, request_t *request, char const *insert, char const *trim, char const *expire)
Definition: rlm_rediswho.c:178
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Named methods exported by a module.
Definition: module.h:173
RETURN_MODULE_FAIL
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
#define talloc_get_type_abort_const
Definition: talloc.h:282
int nonnull(2, 5))