All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: d51aef375fd8027bee0a0a7c56a3020b1a8c35ec $
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: d51aef375fd8027bee0a0a7c56a3020b1a8c35ec $")
30 
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/modules.h>
33 #include <freeradius-devel/modpriv.h>
34 #include <freeradius-devel/rad_assert.h>
35 
36 #include "../rlm_redis/redis.h"
37 #include "../rlm_redis/cluster.h"
38 
39 typedef struct rlm_rediswho {
40  fr_redis_conf_t *conf; //!< Connection parameters for the Redis server.
41  //!< Must be first field in this struct.
42 
43  char const *name; //!< Instance name.
45  fr_redis_cluster_t *cluster; //!< Pool O pools
46 
47  int expiry_time; //!< Expiry time in seconds if no updates are received for a user
48 
49  int trim_count; //!< How many session updates to keep track of per user.
50 
51  char const *insert; //!< Command for inserting session data
52  char const *trim; //!< Command for trimming the session list.
53  char const *expire; //!< Command for expiring entries.
55 
58  { FR_CONF_OFFSET("trim", PW_TYPE_STRING | PW_TYPE_XLAT, rlm_rediswho_t, trim) }, /* required only if trim_count > 0 */
61 };
62 
65 
66  { FR_CONF_OFFSET("trim_count", PW_TYPE_SIGNED, rlm_rediswho_t, trim_count), .dflt = "-1" },
67 
68  /*
69  * These all smash the same variables, because we don't care about them right now.
70  * In 3.1, we should have a way of saying "parse a set of sub-sections according to a template"
71  */
72  { FR_CONF_POINTER("Start", PW_TYPE_SUBSECTION, NULL), .dflt = section_config },
73  { FR_CONF_POINTER("Interim-Update", PW_TYPE_SUBSECTION, NULL), .dflt = section_config },
74  { FR_CONF_POINTER("Stop", PW_TYPE_SUBSECTION, NULL), .dflt = section_config },
75 
77 };
78 
79 /*
80  * Query the database executing a command with no result rows
81  */
82 static int rediswho_command(rlm_rediswho_t *inst, REQUEST *request, char const *fmt)
83 {
84  fr_redis_conn_t *conn;
85 
86  int ret = -1;
87 
89  fr_redis_rcode_t status;
90  redisReply *reply = NULL;
91  int s_ret;
92 
93  uint8_t const *key = NULL;
94  size_t key_len = 0;
95 
96  int argc;
97  char const *argv[MAX_REDIS_ARGS];
98  char argv_buf[MAX_REDIS_COMMAND_LEN];
99 
100  if (!fmt || !*fmt) return 0;
101 
102  argc = rad_expand_xlat(request, fmt, MAX_REDIS_ARGS, argv, false, sizeof(argv_buf), argv_buf);
103  if (argc < 0) return -1;
104 
105  /*
106  * If we've got multiple arguments, the second one is usually the key.
107  * The Redis docs say commands should be analysed first to get key
108  * positions, but this involves sending them to the server, which is
109  * just as expensive as sending them to the wrong server and receiving
110  * a redirect.
111  */
112  if (argc > 1) {
113  key = (uint8_t const *)argv[1];
114  key_len = strlen((char const *)key);
115  }
116 
117  for (s_ret = fr_redis_cluster_state_init(&state, &conn, inst->cluster, request, key, key_len, false);
118  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
119  s_ret = fr_redis_cluster_state_next(&state, &conn, inst->cluster, request, status, &reply)) {
120  reply = redisCommandArgv(conn->handle, argc, argv, NULL);
121  status = fr_redis_command_status(conn, reply);
122  }
123  if (s_ret != REDIS_RCODE_SUCCESS) {
124  RERROR("Failed inserting accounting data");
125  fr_redis_reply_free(reply);
126  return -1;
127  }
128 
129  rad_assert(reply); /* clang scan */
130  switch (reply->type) {
131  case REDIS_REPLY_INTEGER:
132  RDEBUG2("Query response %lld", reply->integer);
133  if (reply->integer > 0) ret = reply->integer;
134  break;
135 
136  case REDIS_REPLY_STRING:
137  REDEBUG2("Query response %s", reply->str);
138  break;
139 
140  default:
141  break;
142  }
143  fr_redis_reply_free(reply);
144 
145  return ret;
146 }
147 
148 static int mod_instantiate(CONF_SECTION *conf, void *instance)
149 {
150  rlm_rediswho_t *inst = instance;
151 
152  inst->cluster = fr_redis_cluster_alloc(inst, conf, inst->conf);
153  if (!inst->cluster) return -1;
154 
155  return 0;
156 }
157 
158 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
159 {
160  rlm_rediswho_t *inst = instance;
161 
163 
164  inst->cs = conf;
165  inst->name = cf_section_name2(conf);
166  if (!inst->name) inst->name = cf_section_name1(conf);
167 
168  return 0;
169 }
170 
172  char const *insert,
173  char const *trim,
174  char const *expire)
175 {
176  int ret;
177 
178  ret = rediswho_command(inst, request, insert);
179  if (ret < 0) return RLM_MODULE_FAIL;
180 
181  /* Only trim if necessary */
182  if ((inst->trim_count >= 0) && (ret > inst->trim_count)) {
183  if (rediswho_command(inst, request, trim) < 0) return RLM_MODULE_FAIL;
184  }
185 
186  if (rediswho_command(inst, request, expire) < 0) return RLM_MODULE_FAIL;
187  return RLM_MODULE_OK;
188 }
189 
190 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
191 {
192  rlm_rediswho_t *inst = instance;
193  rlm_rcode_t rcode;
194  VALUE_PAIR *vp;
195  fr_dict_enum_t *dv;
196  CONF_SECTION *cs;
197  char const *insert, *trim, *expire;
198 
199  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_ACCT_STATUS_TYPE, TAG_ANY);
200  if (!vp) {
201  RDEBUG("Could not find account status type in packet");
202  return RLM_MODULE_NOOP;
203  }
204 
205  dv = fr_dict_enum_by_da(NULL, vp->da, vp->vp_integer);
206  if (!dv) {
207  RDEBUG("Unknown Acct-Status-Type %u", vp->vp_integer);
208  return RLM_MODULE_NOOP;
209  }
210 
211  cs = cf_section_sub_find(inst->cs, dv->name);
212  if (!cs) {
213  RDEBUG("No subsection %s", dv->name);
214  return RLM_MODULE_NOOP;
215  }
216 
217  insert = cf_pair_value(cf_pair_find(cs, "insert"));
218  trim = cf_pair_value(cf_pair_find(cs, "trim"));
219  expire = cf_pair_value(cf_pair_find(cs, "expire"));
220 
221  rcode = mod_accounting_all(inst, request, insert, trim, expire);
222 
223  return rcode;
224 }
225 
226 extern module_t rlm_rediswho;
227 module_t rlm_rediswho = {
229  .name = "rediswho",
230  .type = RLM_TYPE_THREAD_SAFE,
231  .inst_size = sizeof(rlm_rediswho_t),
232  .config = module_config,
233  .instantiate = mod_instantiate,
234  .bootstrap = mod_bootstrap,
235  .methods = {
237  },
238 };
#define RERROR(fmt,...)
Definition: log.h:207
int expiry_time
Expiry time in seconds if no updates are received for a user.
Definition: rlm_rediswho.c:47
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
The module is OK, continue.
Definition: radiusd.h:91
Metadata exported by the module.
Definition: modules.h:134
Configuration parameters for a redis connection.
Definition: redis.h:88
CONF_SECTION * cs
Definition: rlm_rediswho.c:44
32 Bit signed integer.
Definition: radius.h:45
#define MAX_REDIS_ARGS
Definition: redis.h:38
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
fr_redis_conf_t * conf
Connection parameters for the Redis server.
Definition: rlm_rediswho.c:40
#define RLM_MODULE_INIT
Definition: modules.h:86
static rlm_rcode_t mod_accounting_all(rlm_rediswho_t *inst, REQUEST *request, char const *insert, char const *trim, char const *expire)
Definition: rlm_rediswho.c:171
#define REDEBUG2(fmt,...)
Definition: log.h:255
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
#define fr_redis_reply_free(_p)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: redis.h:56
int trim_count
How many session updates to keep track of per user.
Definition: rlm_rediswho.c:49
#define inst
A redis cluster.
Definition: cluster.c:254
#define PW_TYPE_SUBSECTION
Definition: conffile.h:188
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
Definition: conffile.c:3478
char const * cf_pair_value(CONF_PAIR const *pair)
Definition: conffile.c:3506
static CONF_PARSER module_config[]
Definition: rlm_rediswho.c:63
#define MAX_REDIS_COMMAND_LEN
Definition: redis.h:37
#define rad_assert(expr)
Definition: rad_assert.h:38
redisContext * handle
Hiredis context used when issuing commands.
Definition: redis.h:81
module_t rlm_rediswho
Definition: rlm_rediswho.c:227
#define REDIS_COMMON_CONFIG
Definition: redis.h:106
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:52
#define PW_TYPE_XLAT
string will be dynamically expanded.
Definition: conffile.h:207
static CONF_PARSER section_config[]
Definition: rlm_rediswho.c:56
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:76
3 methods index for accounting section.
Definition: modules.h:44
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
char const * name
Instance name.
Definition: rlm_rediswho.c:43
Redis connection sequence state.
Definition: cluster.h:43
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Definition: rlm_rediswho.c:158
char const * expire
Command for expiring entries.
Definition: rlm_rediswho.c:53
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
char name[1]
Enum name.
Definition: dict.h:97
fr_redis_cluster_t * cluster
Pool O pools.
Definition: rlm_rediswho.c:45
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
Module succeeded without doing anything.
Definition: radiusd.h:96
#define RDEBUG2(fmt,...)
Definition: log.h:244
unsigned int state
Definition: proto_bfd.c:200
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 *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
Definition: cluster.c:1714
struct rlm_rediswho rlm_rediswho_t
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
Try the operation again.
Definition: redis.h:70
fr_dict_enum_t * fr_dict_enum_by_da(fr_dict_t *dict, fr_dict_attr_t const *da, int value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition: dict.c:3654
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf)
Allocate and initialise a new cluster structure.
Definition: cluster.c:2059
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: redis.h:67
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
int rad_expand_xlat(REQUEST *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:658
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Definition: rlm_rediswho.c:148
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 *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:1594
char const * trim
Command for trimming the session list.
Definition: rlm_rediswho.c:52
char const * insert
Command for inserting session data.
Definition: rlm_rediswho.c:51
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_rediswho.c:190
String of printable characters.
Definition: radius.h:33
#define FR_CONF_POINTER(_n, _t, _p)
Definition: conffile.h:172
Operation was successfull.
Definition: redis.h:68
#define RCSID(id)
Definition: build.h:135
static int rediswho_command(rlm_rediswho_t *inst, REQUEST *request, char const *fmt)
Definition: rlm_rediswho.c:82
#define RDEBUG(fmt,...)
Definition: log.h:243
Value of an enumerated attribute.
Definition: dict.h:94
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
Connection handle, holding a redis context.
Definition: redis.h:80