The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sqlippool.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: 1561166f23f98d496a4637b082b0038da1e662f7 $
19  * @file rlm_sqlippool.c
20  * @brief Allocates an IPv4 address from pools stored in SQL.
21  *
22  * @copyright 2002 Globe.Net Communications Limited
23  * @copyright 2006 The FreeRADIUS server project
24  * @copyright 2006 Suntel Communications
25  */
26 RCSID("$Id: 1561166f23f98d496a4637b082b0038da1e662f7 $")
27 
28 #define LOG_PREFIX inst->name
29 
30 #include <rlm_sql.h>
31 #include <freeradius-devel/util/debug.h>
32 #include <freeradius-devel/radius/radius.h>
33 #include <freeradius-devel/unlang/function.h>
34 
35 #include <ctype.h>
36 
37 /*
38  * Define a structure for our module configuration.
39  */
40 typedef struct {
41  char const *name;
42  char const *sql_name;
43 
44  rlm_sql_t const *sql;
46 
47 /** Call environment used by module alloc method
48  */
49 typedef struct {
50  fr_value_box_t pool_name; //!< Name of pool address will be allocated from.
51  tmpl_t *pool_name_tmpl; //!< Tmpl used to expand pool_name
52  fr_value_box_t requested_address; //!< IP address being requested by client.
53  tmpl_t *allocated_address_attr; //!< Attribute to populate with allocated IP.
54  fr_value_box_t allocated_address; //!< Existing value for allocated IP.
55  fr_value_box_t begin; //!< SQL query to begin transaction.
56  tmpl_t *existing; //!< tmpl to expand as query for finding the existing IP.
57  tmpl_t *requested; //!< tmpl to expand as query for finding the requested IP.
58  tmpl_t *find; //!< tmpl to expand as query for finding an unused IP.
59  tmpl_t *update; //!< tmpl to expand as query for updating the found IP.
60  tmpl_t *pool_check; //!< tmpl to expand as query for checking for existence of the pool.
61  fr_value_box_t commit; //!< SQL query to commit transaction.
63 
64 /** Call environment used by all other module methods
65  */
66 typedef struct {
67  fr_value_box_t free; //!< SQL query to clear other offered IPs. Only used in "update" method.
68  fr_value_box_t update; //!< SQL query to update an IP record.
70 
71 /** Current step in IP allocation state machine
72  */
73 typedef enum {
74  IPPOOL_ALLOC_EXISTING, //!< Expanding the "existing" query
75  IPPOOL_ALLOC_REQUESTED, //!< Expanding the "requested" query
76  IPPOOL_ALLOC_FIND, //!< Expanding the "find" query
77  IPPOOL_ALLOC_POOL_CHECK, //!< Expanding the "pool_check" query
78  IPPOOL_ALLOC_UPDATE //!< Expanding the "update" query
80 
81 /** Resume context for IP allocation
82  */
83 typedef struct {
84  request_t *request; //!< Current request.
85  ippool_alloc_status_t status; //!< Status of the allocation.
86  ippool_alloc_call_env_t *env; //!< Call environment for the allocation.
87  rlm_sql_handle_t *handle; //!< SQL handle being used for queries.
88  rlm_sql_t const *sql; //!< SQL module instance.
89  fr_value_box_list_t values; //!< Where to put the expanded queries ready for execution.
91 
93  { FR_CONF_OFFSET("sql_module_instance", rlm_sqlippool_t, sql_name), .dflt = "sql" },
94 
96 };
97 
98 static int _sql_escape_uxtx_free(void *uctx)
99 {
100  return talloc_free(uctx);
101 }
102 
103 static void *sql_escape_uctx_alloc(request_t *request, void const *uctx)
104 {
105  static _Thread_local rlm_sql_escape_uctx_t *t_ctx;
106 
107  if (unlikely(t_ctx == NULL)) {
109 
110  MEM(ctx = talloc_zero(NULL, rlm_sql_escape_uctx_t));
112  }
113  t_ctx->sql = uctx;
114  t_ctx->handle = request_data_reference(request, (void *)sql_escape_uctx_alloc, 0);
115 
116  return t_ctx;
117 }
118 
119 /** Perform a single sqlippool query
120  *
121  * Mostly wrapper around sql_query which returns the number of affected rows.
122  *
123  * @param[in] query sql query to execute.
124  * @param[in] handle sql connection handle.
125  * @param[in] sql Instance of rlm_sql.
126  * @param[in] request Current request.
127  * @return
128  * - number of affected rows on success.
129  * - < 0 on error.
130  */
131 static int sqlippool_command(char const *query, rlm_sql_handle_t **handle,
132  rlm_sql_t const *sql, request_t *request)
133 {
134  int ret, affected;
135 
136  /*
137  * If we don't have a command, do nothing.
138  */
139  if (!query || !*query) return 0;
140 
141  /*
142  * No handle? That's an error.
143  */
144  if (!handle || !*handle) return -1;
145 
146  ret = sql->query(sql, request, handle, query);
147  if (ret < 0) return -1;
148 
149  /*
150  * No handle, we can't continue.
151  */
152  if (!*handle) return -1;
153 
154  affected = (sql->driver->sql_affected_rows)(*handle, &sql->config);
155 
156  (sql->driver->sql_finish_query)(*handle, &sql->config);
157 
158  return affected;
159 }
160 
161 /*
162  * Don't repeat yourself
163  */
164 #define DO_PART(_x) if(env->_x.type == FR_TYPE_STRING) { \
165  if(sqlippool_command(env->_x.vb_strvalue, &handle, sql, request) <0) goto error; \
166 }
167 #define DO_AFFECTED(_x, _affected) if (env->_x.type == FR_TYPE_STRING) { \
168  _affected = sqlippool_command(env->_x.vb_strvalue, &handle, sql, request); if (_affected < 0) goto error; \
169 }
170 #define RESERVE_CONNECTION(_handle, _pool, _request) _handle = fr_pool_connection_get(_pool, _request); \
171  if (!_handle) { \
172  REDEBUG("Failed reserving SQL connection"); \
173  RETURN_MODULE_FAIL; \
174  }
175 
176 
177 /*
178  * Query the database expecting a single result row
179  */
180 static int CC_HINT(nonnull (1, 3, 4, 5)) sqlippool_query1(char *out, int outlen, char const *query,
181  rlm_sql_handle_t **handle, rlm_sql_t const *sql,
182  request_t *request)
183 {
184  int rlen, retval;
185  rlm_sql_row_t row;
186 
187  *out = '\0';
188 
189  retval = sql->select(sql, request, handle, query);
190 
191  if ((retval != 0) || !*handle) {
192  REDEBUG("database query error on '%s'", query);
193  return 0;
194  }
195 
196  if (sql->fetch_row(&row, sql, request, handle) < 0) {
197  REDEBUG("Failed fetching query result");
198  goto finish;
199  }
200 
201  if (!row) {
202  RDEBUG2("SQL query did not return any results");
203  goto finish;
204  }
205 
206  if (!row[0]) {
207  REDEBUG("The first column of the result was NULL");
208  goto finish;
209  }
210 
211  rlen = strlen(row[0]);
212  if (rlen >= outlen) {
213  REDEBUG("The first column of the result was too long (%d)", rlen);
214  goto finish;
215  }
216 
217  strcpy(out, row[0]);
218  retval = rlen;
219 
220 finish:
221  (sql->driver->sql_finish_select_query)(*handle, &sql->config);
222 
223  return retval;
224 }
225 
226 static int mod_bootstrap(module_inst_ctx_t const *mctx)
227 {
228  rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t);
229  inst->name = talloc_asprintf(inst, "%s - %s", mctx->inst->name, inst->sql_name);
230 
231  return 0;
232 }
233 
234 /*
235  * Do any per-module initialization that is separate to each
236  * configured instance of the module. e.g. set up connections
237  * to external databases, read configuration files, set up
238  * dictionary entries, etc.
239  *
240  * If configuration information is given in the config section
241  * that must be referenced in later calls, store a handle to it
242  * in *instance otherwise put a null pointer there.
243  */
244 static int mod_instantiate(module_inst_ctx_t const *mctx)
245 {
246  module_instance_t *sql;
247  rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t);
248  CONF_SECTION *conf = mctx->inst->conf;
249 
250  sql = module_rlm_by_name(NULL, inst->sql_name);
251  if (!sql) {
252  cf_log_err(conf, "failed to find sql instance named %s",
253  inst->sql_name);
254  return -1;
255  }
256 
257  inst->sql = (rlm_sql_t *) sql->dl_inst->data;
258 
259  if (strcmp(talloc_get_name(inst->sql), "rlm_sql_t") != 0) {
260  cf_log_err(conf, "Module \"%s\" is not an instance of the rlm_sql module",
261  inst->sql_name);
262  return -1;
263  }
264 
265  return 0;
266 }
267 
268 /** Release SQL pool connections when alloc context is freed.
269  */
271 {
272  (void) request_data_get(to_free->request, (void *)sql_escape_uctx_alloc, 0);
273  if (to_free->handle) fr_pool_connection_release(to_free->sql->pool, to_free->request, to_free->handle);
274  return 0;
275 }
276 
277 #define REPEAT_MOD_ALLOC_RESUME if (unlang_function_repeat_set(request, mod_alloc_resume) < 0) RETURN_MODULE_FAIL
278 
279 /** Resume function called after each IP allocation query is expanded
280  *
281  * Executes the query and, if appropriate, pushes the next tmpl for expansion
282  *
283  * Following the final (successful) query, the destination attribute is populated.
284  *
285  * @param p_result Result of IP allocation.
286  * @param priority Unused.
287  * @param request Current request.
288  * @param uctx Current allocation context.
289  * @return One of the UNLANG_ACTION_* values.
290  */
291 static unlang_action_t mod_alloc_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
292 {
293  ippool_alloc_ctx_t *alloc_ctx = talloc_get_type_abort(uctx, ippool_alloc_ctx_t);
294  ippool_alloc_call_env_t *env = alloc_ctx->env;
295  int allocation_len = 0;
296  char allocation[FR_MAX_STRING_LEN];
297  rlm_sql_handle_t *handle = alloc_ctx->handle;
298  rlm_sql_t const *sql = alloc_ctx->sql;
299  fr_value_box_t *query = fr_value_box_list_pop_head(&alloc_ctx->values);
300 
301  switch (alloc_ctx->status) {
303  if (query) {
304  allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle,
305  alloc_ctx->sql, request);
306  talloc_free(query);
307  if (!handle) {
308  error:
309  talloc_free(alloc_ctx);
311  }
312  if (allocation_len > 0) goto make_pair;
313  }
314 
315  /*
316  * If there's a requested address and associated query, expand that
317  */
318  if (env->requested && (env->requested_address.type != FR_TYPE_NULL)) {
319  alloc_ctx->status = IPPOOL_ALLOC_REQUESTED;
321  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->requested, NULL) < 0) goto error;
323  }
324  goto expand_find;
325 
327  if (query) {
328  allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle,
329  alloc_ctx->sql, request);
330  talloc_free(query);
331  if (!handle) goto error;
332  if (allocation_len > 0) goto make_pair;
333  }
334 
335  expand_find:
336  /*
337  * Neither "existing" nor "requested" found an address, expand "find" query
338  */
339  alloc_ctx->status = IPPOOL_ALLOC_FIND;
341  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->find, NULL) < 0) goto error;
343 
344  case IPPOOL_ALLOC_FIND:
345  {
346  tmpl_t ip_rhs;
347  map_t ip_map;
348 
349  allocation_len = sqlippool_query1(allocation, sizeof(allocation), query->vb_strvalue, &handle,
350  alloc_ctx->sql, request);
351  talloc_free(query);
352  if (!handle) goto error;
353 
354  if (allocation_len == 0) {
355  /*
356  * Nothing found
357  */
358  DO_PART(commit);
359 
360  /*
361  * Should we perform pool-check?
362  */
363  if (env->pool_check) {
364  alloc_ctx->status = IPPOOL_ALLOC_POOL_CHECK;
366  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->pool_check, NULL) < 0) goto error;
368  }
369  no_address:
370  RWDEBUG("IP address could not be allocated");
372  }
373 
374  make_pair:
375  /*
376  * See if we can create the VP from the returned data. If not,
377  * error out. If so, add it to the list.
378  */
379  ip_map = (map_t) {
380  .lhs = env->allocated_address_attr,
381  .op = T_OP_SET,
382  .rhs = &ip_rhs
383  };
384 
385  tmpl_init_shallow(&ip_rhs, TMPL_TYPE_DATA, T_BARE_WORD, "", 0, NULL);
386  fr_value_box_bstrndup_shallow(&ip_map.rhs->data.literal, NULL, allocation, allocation_len, false);
387  if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) {
388  DO_PART(commit);
389 
390  REDEBUG("Invalid IP address [%s] returned from database query.", allocation);
391  goto error;
392  }
393 
394  RDEBUG2("Allocated IP %s", allocation);
395 
396  /*
397  * If we have an update query expand it
398  */
399  if (env->update) {
400  alloc_ctx->status = IPPOOL_ALLOC_UPDATE;
402  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->update, NULL) < 0) goto error;
404  }
405 
406  goto finish;
407  }
408 
410  if (query) {
411  /*
412  *Ok, so the allocate-find query found nothing ...
413  *Let's check if the pool exists at all
414  */
415  allocation_len = sqlippool_query1(allocation, sizeof(allocation),
416  query->vb_strvalue, &handle, sql, request);
417  talloc_free(query);
418  if (!handle) RETURN_MODULE_FAIL;
419 
420  if (allocation_len) {
421  /*
422  * Pool exists after all... So,
423  * the failure to allocate the IP
424  * address was most likely due to
425  * the depletion of the pool. In
426  * that case, we should return
427  * NOTFOUND
428  */
429  RWDEBUG("Pool \"%pV\" appears to be full", &env->pool_name);
431  }
432 
433  /*
434  * Pool doesn't exist in the table. It
435  * may be handled by some other instance of
436  * sqlippool, so we should just ignore this
437  * allocation failure and return NOOP
438  */
439  RWDEBUG("IP address could not be allocated as no pool exists with the name \"%pV\"",
440  &env->pool_name);
442  }
443  goto no_address;
444 
445  case IPPOOL_ALLOC_UPDATE:
446  if (query) {
447  if (sqlippool_command(query->vb_strvalue, &handle, sql, request) < 0) goto error;
448  talloc_free(query);
449  }
450 
451  finish:
452  DO_PART(commit);
453 
454  talloc_free(alloc_ctx);
456  }
457 
458  /*
459  * All return paths are handled within the switch statement.
460  */
461  fr_assert(0);
463 }
464 
465 /** Initiate the allocation of an IP address from the pool.
466  *
467  * Based on configured queries and attributes which exist, determines the first
468  * query tmpl to expand.
469  *
470  * @param p_result Result of the allocation (if it fails).
471  * @param mctx Module context.
472  * @param request Current request.
473  * @return One of the UNLANG_ACTION_* values.
474  */
475 static unlang_action_t CC_HINT(nonnull) mod_alloc(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
476 {
477  rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t);
478  ippool_alloc_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_alloc_call_env_t);
479  rlm_sql_t const *sql = inst->sql;
480  rlm_sql_handle_t *handle;
481  ippool_alloc_ctx_t *alloc_ctx = NULL;
482 
483  /*
484  * If the allocated IP attribute already exists, do nothing
485  */
486  if (env->allocated_address.type) {
487  RDEBUG2("%s already exists (%pV)", env->allocated_address_attr->name, &env->allocated_address);
489  }
490 
491  if (env->pool_name.type == FR_TYPE_NULL) {
492  RDEBUG2("No %s defined", env->pool_name_tmpl->name);
494  }
495 
496  RESERVE_CONNECTION(handle, inst->sql->pool, request);
497  request_data_add(request, (void *)sql_escape_uctx_alloc, 0, handle, false, false, false);
498 
499  DO_PART(begin);
500 
501  MEM(alloc_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ippool_alloc_ctx_t));
502  *alloc_ctx = (ippool_alloc_ctx_t) {
503  .env = env,
504  .handle = handle,
505  .sql = inst->sql,
506  .request = request,
507  };
508  talloc_set_destructor(alloc_ctx, sqlippool_alloc_ctx_free);
509  fr_value_box_list_init(&alloc_ctx->values);
510  if (unlang_function_push(request, NULL, mod_alloc_resume, NULL, 0, UNLANG_SUB_FRAME, alloc_ctx) < 0 ) {
511  error:
512  talloc_free(alloc_ctx);
514  }
515 
516  /*
517  * Establish which tmpl needs expanding first.
518  *
519  * If there is a query for finding the existing IP expand that first
520  */
521  if (env->existing) {
522  alloc_ctx->status = IPPOOL_ALLOC_EXISTING;
523  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->existing, NULL) < 0) goto error;
525  }
526 
527  /*
528  * If have a requested IP address and a query to find whether it is available then try that
529  */
530  if (env->requested && (env->requested_address.type != FR_TYPE_NULL)) {
531  alloc_ctx->status = IPPOOL_ALLOC_REQUESTED;
532  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->requested, NULL) < 0) goto error;
534  }
535 
536  /*
537  * If neither of the previous two queries were defined, first expand the "find" query
538  */
539  alloc_ctx->status = IPPOOL_ALLOC_FIND;
540  if (unlang_tmpl_push(alloc_ctx, &alloc_ctx->values, request, env->find, NULL) < 0) goto error;
542 }
543 
544 /** Common function used by module methods which perform an optional "free" then "update"
545  * - update
546  * - release
547  * - bulk_release
548  * - mark
549  */
550 static unlang_action_t CC_HINT(nonnull) mod_common(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
551 {
552  rlm_sqlippool_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_sqlippool_t);
553  ippool_common_call_env_t *env = talloc_get_type_abort(mctx->env_data, ippool_common_call_env_t);
554  rlm_sql_t const *sql = inst->sql;
555  rlm_sql_handle_t *handle;
556  int affected = 0;
557 
558  RESERVE_CONNECTION(handle, inst->sql->pool, request);
559 
560  /*
561  * An optional query which can be used to tidy up before updates
562  * primarily intended for multi-server setups sharing a common database
563  * allowing for tidy up of multiple offered addresses in a DHCP context.
564  */
565  DO_PART(free);
566 
567  DO_AFFECTED(update, affected);
568 
569  if (handle) fr_pool_connection_release(inst->sql->pool, request, handle);
570 
571  if (affected > 0) RETURN_MODULE_UPDATED;
573 
574 error:
575  if (handle) fr_pool_connection_release(inst->sql->pool, request, handle);
577 }
578 
579 /** Call SQL module box_escape_func to escape tainted values
580  */
581 static int sqlippool_box_escape(fr_value_box_t *vb, void *uctx) {
582  rlm_sql_escape_uctx_t *ctx = talloc_get_type_abort(uctx, rlm_sql_escape_uctx_t);
583 
584  return ctx->sql->box_escape_func(vb, uctx);
585 }
586 
587 /** Custom parser for sqlippool call env
588  *
589  * Needed as the escape function needs to reference
590  * the correct instance of the SQL module since escaping functions
591  * are dependent on the driver used by a given module instance.
592  */
593 static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
594  UNUSED char const *section_name1, UNUSED char const *section_name2,
595  void const *data, UNUSED call_env_parser_t const *rule)
596 {
599  rlm_sql_t const *sql;
600  tmpl_t *parsed_tmpl;
601  CONF_PAIR const *to_parse = cf_item_to_pair(ci);
602  tmpl_rules_t our_rules = *t_rules;
603 
604  /*
605  * Lookup the sql module instance.
606  */
607  sql_inst = module_rlm_by_name(NULL, inst->sql_name);
608  if (!sql_inst) return -1;
609  sql = talloc_get_type_abort(sql_inst->dl_inst->data, rlm_sql_t);
610 
611  /*
612  * Set the sql module instance data as the uctx for escaping
613  * and use the same "safe_for" as the sql module.
614  */
615  our_rules.escape.uctx.func.uctx = sql;
616  our_rules.escape.safe_for = (fr_value_box_safe_for_t)sql->driver;
618 
619  if (tmpl_afrom_substr(ctx, &parsed_tmpl,
620  &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
621  cf_pair_value_quote(to_parse), NULL, &our_rules) < 0) return -1;
622  *(void **)out = parsed_tmpl;
623  return 0;
624 }
625 
626 #define QUERY_ESCAPE .pair.escape = { \
627  .func = sqlippool_box_escape, \
628  .mode = TMPL_ESCAPE_PRE_CONCAT, \
629  .uctx = { .func = { .alloc = sql_escape_uctx_alloc }, .type = TMPL_ESCAPE_UCTX_ALLOC_FUNC }, \
630 }, .pair.func = call_env_parse
631 
634  .env = (call_env_parser_t[]) {
636  ippool_alloc_call_env_t, pool_name, pool_name_tmpl),
637  .pair.dflt = "&control.IP-Pool.Name", .pair.dflt_quote = T_BARE_WORD },
639  ippool_alloc_call_env_t, requested_address) },
640  { FR_CALL_ENV_PARSE_OFFSET("allocated_address_attr", FR_TYPE_VOID,
642  ippool_alloc_call_env_t, allocated_address, allocated_address_attr) },
648  ippool_alloc_call_env_t, requested), QUERY_ESCAPE },
654  ippool_alloc_call_env_t, pool_check), QUERY_ESCAPE },
658  }
659 };
660 
663  .env = (call_env_parser_t[]) {
669  }
670 };
671 
674  .env = (call_env_parser_t[]) {
678  }
679 };
680 
683  .env = (call_env_parser_t[]) {
687  }
688 };
689 
692  .env = (call_env_parser_t[]) {
696  }
697 };
698 
699 /*
700  * The module name should be the only globally exported symbol.
701  * That is, everything else should be 'static'.
702  *
703  * If the module needs to temporarily modify it's instantiation
704  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
705  * The server will then take care of ensuring that the module
706  * is single-threaded.
707  */
710  .common = {
711  .magic = MODULE_MAGIC_INIT,
712  .name = "sqlippool",
713  .flags = MODULE_TYPE_THREAD_SAFE,
714  .inst_size = sizeof(rlm_sqlippool_t),
716  .bootstrap = mod_bootstrap,
718  },
719  .method_names = (module_method_name_t[]){
720  /*
721  * RADIUS specific
722  */
723  { .name1 = "recv", .name2 = "access-request", .method = mod_alloc,
724  .method_env = &sqlippool_alloc_method_env },
725  { .name1 = "accounting", .name2 = "start", .method = mod_common,
726  .method_env = &sqlippool_update_method_env },
727  { .name1 = "accounting", .name2 = "alive", .method = mod_common,
728  .method_env = &sqlippool_update_method_env },
729  { .name1 = "accounting", .name2 = "stop", .method = mod_common,
730  .method_env = &sqlippool_release_method_env },
731  { .name1 = "accounting", .name2 = "accounting-on", .method = mod_common,
732  .method_env = &sqlippool_bulk_release_method_env },
733  { .name1 = "accounting", .name2 = "accounting-off", .method = mod_common,
734  .method_env = &sqlippool_bulk_release_method_env },
735 
736  /*
737  * DHCPv4
738  */
739  { .name1 = "recv", .name2 = "Discover", .method = mod_alloc,
740  .method_env = &sqlippool_alloc_method_env },
741  { .name1 = "recv", .name2 = "Request", .method = mod_common,
742  .method_env = &sqlippool_update_method_env },
743  { .name1 = "recv", .name2 = "Confirm", .method = mod_common,
744  .method_env = &sqlippool_update_method_env },
745  { .name1 = "recv", .name2 = "Rebind", .method = mod_common,
746  .method_env = &sqlippool_update_method_env },
747  { .name1 = "recv", .name2 = "Renew", .method = mod_common,
748  .method_env = &sqlippool_update_method_env },
749  { .name1 = "recv", .name2 = "Release", .method = mod_common,
750  .method_env = &sqlippool_release_method_env },
751  { .name1 = "recv", .name2 = "Decline", .method = mod_common,
752  .method_env = &sqlippool_mark_method_env },
753 
754  /*
755  * Generic
756  */
757  { .name1 = "recv", .name2 = CF_IDENT_ANY, .method = mod_common,
758  .method_env = &sqlippool_update_method_env },
759  { .name1 = "send", .name2 = CF_IDENT_ANY, .method = mod_alloc,
760  .method_env = &sqlippool_alloc_method_env },
761 
762  /*
763  * Named methods matching module operations
764  */
765  { .name1 = "allocate", .name2 = CF_IDENT_ANY, .method = mod_alloc,
766  .method_env = &sqlippool_alloc_method_env },
767  { .name1 = "update", .name2 = CF_IDENT_ANY, .method = mod_common,
768  .method_env = &sqlippool_update_method_env },
769  { .name1 = "renew", .name2 = CF_IDENT_ANY, .method = mod_common,
770  .method_env = &sqlippool_update_method_env },
771  { .name1 = "release", .name2 = CF_IDENT_ANY, .method = mod_common,
772  .method_env = &sqlippool_release_method_env },
773  { .name1 = "bulk-release", .name2 = CF_IDENT_ANY, .method = mod_common,
774  .method_env = &sqlippool_bulk_release_method_env },
775  { .name1 = "mark", .name2 = CF_IDENT_ANY, .method = mod_common,
776  .method_env = &sqlippool_mark_method_env },
777 
779  }
780 
781 };
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
strcpy(log_entry->msg, buffer)
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition: atexit.h:221
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
Definition: call_env.h:341
#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:216
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition: call_env.h:74
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
Definition: call_env.h:84
@ CALL_ENV_FLAG_PARSE_ONLY
The result of parsing will not be evaluated at runtime.
Definition: call_env.h:83
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:73
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:78
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition: call_env.h:316
#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:365
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#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:563
Common header for all CONF_* types.
Definition: cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition: cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:89
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition: cf_util.c:1555
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define CF_IDENT_ANY
Definition: cf_util.h:78
char const *_CONST name
Instance name.
Definition: dl_module.h:163
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
CONF_SECTION *_CONST conf
Module's instance configuration.
Definition: dl_module.h:166
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
free(array)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1384
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define RWDEBUG(fmt,...)
Definition: log.h:361
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition: map.c:1489
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition: map.c:1783
talloc_free(reap)
struct map_s map_t
Definition: map.h:33
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
Definition: merged_model.c:81
@ FR_TYPE_VOID
User data.
Definition: merged_model.c:127
void * env_data
Per call environment data.
Definition: module_ctx.h:44
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
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:51
Specifies a module method identifier.
Definition: module_method.c:36
module_instance_t * module_rlm_by_name(module_instance_t const *parent, char const *asked_name)
Definition: module_rlm.c:785
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition: pool.c:1405
static const conf_parser_t config[]
Definition: base.c:188
#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_UPDATED
Definition: rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
#define RETURN_MODULE_NOTFOUND
Definition: rcode.h:61
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request_data.c:292
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
Definition: request_data.c:339
#define request_data_add(_request, _unique_ptr, _unique_int, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
Definition: request_data.h:59
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1312
Prototypes and functions for the SQL module.
char ** rlm_sql_row_t
Definition: rlm_sql.h:57
rlm_sql_t const * sql
Definition: rlm_sql.h:143
static void * sql_escape_uctx_alloc(request_t *request, void const *uctx)
fr_value_box_t requested_address
IP address being requested by client.
Definition: rlm_sqlippool.c:52
#define RESERVE_CONNECTION(_handle, _pool, _request)
static int sqlippool_query1(char *out, int outlen, char const *query, rlm_sql_handle_t **handle, rlm_sql_t const *sql, request_t *request)
#define REPEAT_MOD_ALLOC_RESUME
tmpl_t * pool_check
tmpl to expand as query for checking for existence of the pool.
Definition: rlm_sqlippool.c:60
#define QUERY_ESCAPE
rlm_sql_handle_t * handle
SQL handle being used for queries.
Definition: rlm_sqlippool.c:87
static const call_env_method_t sqlippool_bulk_release_method_env
static int _sql_escape_uxtx_free(void *uctx)
Definition: rlm_sqlippool.c:98
fr_value_box_t allocated_address
Existing value for allocated IP.
Definition: rlm_sqlippool.c:54
rlm_sql_t const * sql
Definition: rlm_sqlippool.c:44
fr_value_box_t free
SQL query to clear other offered IPs. Only used in "update" method.
Definition: rlm_sqlippool.c:67
#define DO_AFFECTED(_x, _affected)
fr_value_box_list_t values
Where to put the expanded queries ready for execution.
Definition: rlm_sqlippool.c:89
static const call_env_method_t sqlippool_release_method_env
static const call_env_method_t sqlippool_update_method_env
static int sqlippool_command(char const *query, rlm_sql_handle_t **handle, rlm_sql_t const *sql, request_t *request)
Perform a single sqlippool query.
static unlang_action_t mod_alloc(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Initiate the allocation of an IP address from the pool.
char const * sql_name
Definition: rlm_sqlippool.c:42
ippool_alloc_call_env_t * env
Call environment for the allocation.
Definition: rlm_sqlippool.c:86
static int mod_bootstrap(module_inst_ctx_t const *mctx)
static const call_env_method_t sqlippool_alloc_method_env
#define DO_PART(_x)
request_t * request
Current request.
Definition: rlm_sqlippool.c:84
static unlang_action_t mod_common(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Common function used by module methods which perform an optional "free" then "update".
static const call_env_method_t sqlippool_mark_method_env
char const * name
Definition: rlm_sqlippool.c:41
static unlang_action_t mod_alloc_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Resume function called after each IP allocation query is expanded.
static int sqlippool_box_escape(fr_value_box_t *vb, void *uctx)
Call SQL module box_escape_func to escape tainted values.
tmpl_t * pool_name_tmpl
Tmpl used to expand pool_name.
Definition: rlm_sqlippool.c:51
fr_value_box_t commit
SQL query to commit transaction.
Definition: rlm_sqlippool.c:61
tmpl_t * existing
tmpl to expand as query for finding the existing IP.
Definition: rlm_sqlippool.c:56
tmpl_t * requested
tmpl to expand as query for finding the requested IP.
Definition: rlm_sqlippool.c:57
fr_value_box_t pool_name
Name of pool address will be allocated from.
Definition: rlm_sqlippool.c:50
static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, void const *data, UNUSED call_env_parser_t const *rule)
Custom parser for sqlippool call env.
static int sqlippool_alloc_ctx_free(ippool_alloc_ctx_t *to_free)
Release SQL pool connections when alloc context is freed.
fr_value_box_t update
SQL query to update an IP record.
Definition: rlm_sqlippool.c:68
fr_value_box_t begin
SQL query to begin transaction.
Definition: rlm_sqlippool.c:55
tmpl_t * find
tmpl to expand as query for finding an unused IP.
Definition: rlm_sqlippool.c:58
tmpl_t * allocated_address_attr
Attribute to populate with allocated IP.
Definition: rlm_sqlippool.c:53
ippool_alloc_status_t status
Status of the allocation.
Definition: rlm_sqlippool.c:85
module_rlm_t rlm_sqlippool
ippool_alloc_status_t
Current step in IP allocation state machine.
Definition: rlm_sqlippool.c:73
@ IPPOOL_ALLOC_POOL_CHECK
Expanding the "pool_check" query.
Definition: rlm_sqlippool.c:77
@ IPPOOL_ALLOC_REQUESTED
Expanding the "requested" query.
Definition: rlm_sqlippool.c:75
@ IPPOOL_ALLOC_FIND
Expanding the "find" query.
Definition: rlm_sqlippool.c:76
@ IPPOOL_ALLOC_EXISTING
Expanding the "existing" query.
Definition: rlm_sqlippool.c:74
@ IPPOOL_ALLOC_UPDATE
Expanding the "update" query.
Definition: rlm_sqlippool.c:78
static int mod_instantiate(module_inst_ctx_t const *mctx)
static conf_parser_t module_config[]
Definition: rlm_sqlippool.c:92
tmpl_t * update
tmpl to expand as query for updating the found IP.
Definition: rlm_sqlippool.c:59
rlm_sql_t const * sql
SQL module instance.
Definition: rlm_sqlippool.c:88
Call environment used by module alloc method.
Definition: rlm_sqlippool.c:49
Resume context for IP allocation.
Definition: rlm_sqlippool.c:83
Call environment used by all other module methods.
Definition: rlm_sqlippool.c:66
#define FR_SBUFF_IN(_start, _len_or_end)
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
dl_module_inst_t * dl_inst
Structure containing the module's instance data, configuration, and dl handle.
Definition: module.h:183
Per instance data.
Definition: module.h:169
tmpl_escape_t escape
How escaping should be handled during evaluation.
Definition: tmpl.h:358
fr_value_box_safe_for_t literals_safe_for
safe_for value assigned to literal values in xlats, execs, and data.
Definition: tmpl.h:356
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition: tmpl.h:142
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules))
Initialise a tmpl without copying the input name string.
Optional arguments passed to vp_tmpl functions.
Definition: tmpl.h:341
RETURN_MODULE_FAIL
if(!subtype_vp) goto fail
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
Value pair map.
Definition: map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:79
int(* sql_affected_rows)(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
Definition: rlm_sql.h:163
sql_rcode_t(* sql_finish_query)(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
Definition: rlm_sql.h:171
fr_pool_t * pool
Definition: rlm_sql.h:179
fr_value_box_escape_t box_escape_func
Definition: rlm_sql.h:189
rlm_sql_config_t config
Definition: rlm_sql.h:178
rlm_sql_driver_t const * driver
Driver's exported interface.
Definition: rlm_sql.h:186
sql_rcode_t(* query)(rlm_sql_t const *inst, request_t *request, rlm_sql_handle_t **handle, char const *query)
Definition: rlm_sql.h:190
#define talloc_get_type_abort_const
Definition: talloc.h:270
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
Definition: tmpl.c:259
fr_value_box_escape_t func
How to escape when returned from evaluation.
Definition: tmpl_escape.h:81
struct tmpl_escape_t::@72 uctx
fr_value_box_safe_for_t safe_for
Value to set on boxes which have been escaped by the fr_value_box_escape_t function.
Definition: tmpl_escape.h:83
@ T_BARE_WORD
Definition: token.h:120
@ T_OP_SET
Definition: token.h:84
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition: value.c:4181
static fr_slen_t data
Definition: value.h:1259
#define FR_MAX_STRING_LEN
Definition: value.h:30
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition: value.h:155
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:984