All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_sqlhpwippool.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: e895213d9f998859d1143cc2b2d8e369c0541982 $
19  * @file rlm_sqlhpwippool.c
20  * @brief Allocates an IPv4 address from pools defined in ASN Netvim.
21  *
22  * Current bugs/limits:
23  * - probably works only with newer versions of MySQL (subqueries)
24  * - requires FreeRADIUS' SQL user to have proper permissions on proper tables
25  * from Netvim database
26  * - of course uses dirty hacks to get access to the database
27  * - queries and table names are not configurable
28  * - IPv4 only (I don't even care about IPv6 by now)
29  * - pool names (fetched from database) are not "escaped"
30  * - you have to set encoding of radius.acctuniqueid to same as
31  * netvim.ips.rsv_by
32  *
33  * @copyright 2005-2006 Pawel Foremski <pjf@asn.pl>,
34  * @copyright 2000-2006 The FreeRADIUS server project
35  */
36 #include <freeradius-devel/radiusd.h>
37 #include <freeradius-devel/modules.h>
38 #include <freeradius-devel/modpriv.h>
39 
40 #include <ctype.h>
41 
42 #include "rlm_sql.h"
43 
44 #define VENDORPEC_ASN 23782
45 #define ASN_IP_POOL_NAME 1
46 
47 #define RLM_NETVIM_MAX_ROWS 1000000
48 #define RLM_NETVIM_TMP_PREFIX "auth-tmp-"
49 
50 #define MAX_QUERY_LEN 4096
51 
52 RCSID("$Id: e895213d9f998859d1143cc2b2d8e369c0541982 $")
53 
54 typedef struct rlm_sqlhpwippool_t {
55  char const *myname; //!< Name of this instance
58 #ifdef HAVE_PTHREAD_D
59  pthread_mutex_t mutex; //!< Used "with" sync_after
60 #endif
61  uint32_t sincesync; //!< req. done so far since last free IP sync.
62 
63  /* from config */
64  char const *sql_instance_name; //!< rlm_sql instance to use.
65  char const *db_name; //!< Netvim database.
66  bool no_free_fail; //!< Fail if no free IP addresses found.
67  uint32_t free_after; //!< How many seconds an IP should not be used after freeing.
68  uint32_t sync_after; //!< How often to sync with radacct.
70 
71 /* char *name, int type,
72  * size_t offset, void *data, char *dflt */
74  { FR_CONF_OFFSET("sql_module_instance", PW_TYPE_STRING, rlm_sqlhpwippool_t, sql_instance_name), .dflt = "sql" },
75  { FR_CONF_OFFSET("db_name", PW_TYPE_STRING, rlm_sqlhpwippool_t, db_name), .dflt = "netvim" },
76  { FR_CONF_OFFSET("no_free_fail", PW_TYPE_BOOLEAN, rlm_sqlhpwippool_t, no_free_fail), .dflt = "yes" },
77  { FR_CONF_OFFSET("free_after", PW_TYPE_INTEGER, rlm_sqlhpwippool_t, free_after), .dflt = "300" },
78  { FR_CONF_OFFSET("sync_after", PW_TYPE_INTEGER, rlm_sqlhpwippool_t, sync_after), .dflt = "25" },
80 };
81 
82 DIAG_OFF(format-nonliteral)
83 /* handy SQL query tool */
85  rlm_sql_handle_t *sqlsock, char const *fmt, va_list ap)
86 {
87  char query[MAX_QUERY_LEN];
88  vsnprintf(query, MAX_QUERY_LEN, fmt, ap);
89 
90  if (!sqlsock) return 0;
91 
92  if (rlm_sql_query(data->sql_inst, NULL, &sqlsock, query)) {
93  return 0;
94  }
95 
96  return 1;
97 }
98 DIAG_ON(format-nonliteral)
99 
100 /* wrapper around nvp_vquery */
102  rlm_sql_handle_t *sqlsock, char const *fmt, ...)
103 {
104  int r;
105  va_list ap;
106 
107  va_start(ap, fmt);
108  r = nvp_vquery(data, sqlsock, fmt, ap);
109  va_end(ap);
110 
111  return r;
112 }
113 
114 /* handy wrapper around data->db->sql_finish_query() */
116 {
117  return (data->db->sql_finish_query)(sqlsock, data->sql_inst->config);
118 }
119 
120 /* executes query and fetches first row
121  * -1 on no results
122  * 0 on db error
123  * 1 on success */
125  rlm_sql_handle_t *sqlsock, char const *fmt, ...)
126 {
127  va_list ap;
128 
129  va_start(ap, fmt);
130  if (!nvp_vquery(data, sqlsock, fmt, ap)) {
131  va_end(ap);
132  return 0;
133  }
134  va_end(ap);
135 
136  if (data->db->sql_store_result && (data->db->sql_store_result)(sqlsock, data->sql_inst->config)) {
137  radlog(L_ERR,
138  "nvp_select(): error while saving results of query");
139  return 0;
140  }
141 
142  if (data->db->sql_num_rows && ((data->db->sql_num_rows)(sqlsock, data->sql_inst->config) < 1)) {
143  radlog(L_DBG,
144  "nvp_select(): no results in query");
145  return -1;
146  }
147 
148  if ((data->db->sql_fetch_row)(row, sqlsock, data->sql_inst->config)) {
149  radlog(L_ERR, "nvp_select(): couldn't fetch row "
150  "from results of query");
151  return 0;
152  }
153 
154  return 1;
155 }
156 
158 {
159  return ((data->db->sql_free_result)(sqlsock, data->sql_inst->config) ||
160  nvp_finish(data, sqlsock));
161 }
162 
163 /* frees IPs of closed sessions (eg. by external modifications to db) */
165 {
166  if (!nvp_query(data, sqlsock,
167  "UPDATE `%s`.`ips`, `radacct` "
168  "SET "
169  "`ips`.`rsv_until` = `radacct`.`acctstoptime` + INTERVAL %u SECOND "
170  "WHERE "
171  "`radacct`.`acctstoptime` IS NOT NULL AND " /* session is closed */
172  "(" /* address is being used */
173  "`ips`.`pid` IS NOT NULL AND "
174  "(`rsv_until` = 0 OR `rsv_until` > NOW())"
175  ") AND "
176  "`radacct`.`acctuniqueid` = `ips`.`rsv_by`",
177  data->db_name, data->free_after)) {
178  return 0;
179  }
180 
181  nvp_finish(data, sqlsock);
182  return 1;
183 }
184 
185 /* updates number of free IP addresses in pools */
187 {
188  if (!nvp_query(data, sqlsock,
189  "UPDATE `%s`.`ip_pools` "
190  "SET `ip_pools`.`free` = "
191  "(SELECT COUNT(*) "
192  "FROM `%1$s`.`ips` "
193  "WHERE "
194  "`ips`.`ip` BETWEEN "
195  "`ip_pools`.`ip_start` AND `ip_pools`.`ip_stop` AND "
196  "("
197  "`ips`.`pid` IS NULL OR "
198  "(`ips`.`rsv_until` > 0 AND `ips`.`rsv_until` < NOW())"
199  "))",
200  data->db_name)) {
201  return 0;
202  }
203 
204  nvp_finish(data, sqlsock);
205  return 1;
206 }
207 
208 /* cleanup IP pools and sync them with radacct */
210 {
211  rlm_sql_handle_t *sqlsock;
212 
213  /* initialize the SQL socket */
214  sqlsock = fr_connection_get(data->sql_inst->pool);
215  if (!sqlsock) {
216  radlog(L_ERR, "nvp_cleanup(): error while "
217  "requesting new SQL connection");
218  return 0;
219  }
220 
221  /* free IPs of closed sessions */
222  if (!nvp_freeclosed(data, sqlsock)) {
223  fr_connection_release(data->sql_inst->pool, sqlsock);
224  return 0;
225  }
226 
227  /* add sessions opened in the meantime */
228  if (!nvp_query(data, sqlsock,
229  "UPDATE `%s`.`ips`, `radacct` "
230  "SET "
231  "`ips`.`pid` = 0, "
232  "`ips`.`rsv_by` = `radacct`.`acctuniqueid`, "
233  "`ips`.`rsv_since` = `radacct`.`acctstarttime`, "
234  "`ips`.`rsv_until` = 0 "
235  "WHERE "
236  "`radacct`.`acctstoptime` IS NULL AND " /* session is opened */
237  "`ips`.`ip` = INET_ATON(`radacct`.`framedipaddress`) AND "
238  "("
239  "`ips`.`pid` IS NULL OR "
240 /* "(`ips`.`rsv_until` > 0 AND `ips.`rsv_until` < NOW()) " */
241  "`ips`.`rsv_until` != 0" /* no acct pkt received yet */
242  ")",
243  data->db_name)) {
244  fr_connection_release(data->sql_inst->pool, sqlsock);
245  return 0;
246  }
247  else {
248  nvp_finish(data, sqlsock);
249  }
250 
251  /* count number of free IP addresses in IP pools */
252  if (!nvp_syncfree(data, sqlsock)) {
253  fr_connection_release(data->sql_inst->pool, sqlsock);
254  return 0;
255  }
256 
257  fr_connection_release(data->sql_inst->pool, sqlsock);
258  return 1;
259 }
260 
261 /* standard foobar code */
262 static int mod_instantiate(CONF_SECTION *conf, void *instance)
263 {
264  rlm_sqlhpwippool_t *inst = instance;
266 
267  /* save my name */
268  inst->myname = cf_section_name2(conf);
269  if (!inst->myname) {
270  inst->myname = "(no name)";
271  }
272 
273  inst->sincesync = 0;
274 
276  if (!sql_inst) {
277  cf_log_err_cs(conf, "Cannot find SQL module instance "
278  "named \"%s\"",
279  inst->sql_instance_name);
280  return -1;
281  }
282 
283  /* check if the given instance is really a rlm_sql instance */
284  if (strcmp(sql_inst->entry->name, "rlm_sql") != 0) {
285  cf_log_err_cs(conf, "Module \"%s\""
286  " is not an instance of the rlm_sql module",
287  inst->sql_instance_name);
288  return -1;
289  }
290 
291  /* save pointers to useful "objects" */
292  inst->sql_inst = (rlm_sql_t *) sql_inst->insthandle;
293  inst->db = (rlm_sql_module_t *) inst->sql_inst->module;
294 
295  return ((nvp_cleanup(inst)) ? 0 : -1);
296 }
297 
298 /* assign new IP address, if required */
299 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
300 {
301  VALUE_PAIR *vp;
302  char const *pname; /* name of requested IP pool */
303  uint32_t nasip; /* NAS IP in host byte order */
304  struct in_addr ip = {0}; /* reserved IP for client (net. byte order) */
305  rlm_sql_handle_t *sqlsock;
306  unsigned long s_gid, /* _s_elected in sql result set */
307  s_prio, /* as above */
308  s_pid, /* as above */
309  gid, /* real integer value */
310  pid, /* as above */
311  weights_sum, used_sum, ip_start, ip_stop, connid;
312  long prio;
313  rlm_sql_row_t row;
314 
316 
317  /* if IP is already there, then nothing to do */
318  vp = fr_pair_find_by_num(request->reply->vps, 0, PW_FRAMED_IP_ADDRESS, TAG_ANY);
319  if (vp) {
320  radlog(L_DBG,
321  "mod_post_auth(): IP address "
322  "already in the reply packet - exiting");
323  return RLM_MODULE_NOOP;
324  }
325 
326  /* if no pool name, we don't need to do anything */
327  vp = fr_pair_find_by_num(request->reply->vps, VENDORPEC_ASN, ASN_IP_POOL_NAME, TAG_ANY);
328  if (vp) {
329  pname = vp->vp_strvalue;
330  radlog(L_DBG,
331  "mod_post_auth(): pool name = '%s'",
332  pname);
333  }
334  else {
335  radlog(L_DBG,
336  "mod_post_auth(): no IP pool name - exiting");
337  return RLM_MODULE_NOOP;
338  }
339 
340  /* if no NAS IP address, assign 0 */
341  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_NAS_IP_ADDRESS, TAG_ANY);
342  if (vp) {
343  nasip = ntohl(vp->vp_ipaddr);
344  }
345  else {
346  nasip = 0;
347  radlog(L_DBG,
348  "mod_post_auth(): no NAS IP address in "
349  "the request packet - using \"0.0.0.0/0\" (any)");
350  }
351 
352  /* get our database connection */
353  sqlsock = fr_connection_get(inst->sql_inst->pool);
354  if (!sqlsock) {
355  radlog(L_ERR,
356  "mod_post_auth(): error while requesting an SQL socket");
357  return RLM_MODULE_FAIL;
358  }
359 
360  /* get connection id as temporary unique integer */
361  if (nvp_select(&row, inst, sqlsock, "SELECT CONNECTION_ID()") < 1) {
362  radlog(L_ERR, "mod_post_auth(): WTF ;-)!");
363  nvp_select_finish(inst, sqlsock);
364  fr_connection_release(inst->sql_inst->pool, sqlsock);
365  return RLM_MODULE_FAIL;
366  }
367 
368  connid = strtoul(row[0], (char **) NULL, 10);
369  nvp_select_finish(inst, sqlsock);
370 
371  /* synchronize with radacct db, if needed */
372  if (++inst->sincesync >= inst->sync_after
373 #ifdef HAVE_PTHREAD_D
374  && (pthread_mutex_trylock(&inst->mutex)) == 0
375 #endif
376  ) {
377  int r;
378 
379  inst->sincesync = 0;
380 
381  radlog(L_DBG,
382  "mod_post_auth(): syncing with radacct table");
383 
384  r = (nvp_freeclosed(inst, sqlsock) && nvp_syncfree(inst, sqlsock));
385 
386 #ifdef HAVE_PTHREAD_D
387  pthread_mutex_unlock(&inst->mutex);
388 #endif
389 
390  if (!r) {
391  radlog(L_ERR,
392  "mod_post_auth(): synchronization failed");
393  fr_connection_release(inst->sql_inst->pool, sqlsock);
394  return RLM_MODULE_FAIL;
395  }
396  }
397 
398  for (s_gid = 0; s_gid < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_gid++) {
399  radlog(L_DBG,
400  "mod_post_auth(): selecting gid on position %lu",
401  s_gid);
402 
403  /* find the most specific group which NAS belongs to */
404  switch (nvp_select(&row, inst, sqlsock,
405  "SELECT `host_groups`.`gid` "
406  "FROM "
407  "`%s`.`host_groups`, "
408  "`%1$s`.`gid_ip`, "
409  "`%1$s`.`ids` "
410  "WHERE "
411  "`host_groups`.`gid` = `ids`.`id` AND "
412  "`ids`.`enabled` = 1 AND "
413  "`host_groups`.`gid` = `gid_ip`.`gid` AND "
414  "%lu BETWEEN `gid_ip`.`ip_start` AND `gid_ip`.`ip_stop` "
415  "ORDER BY (`gid_ip`.`ip_stop` - `gid_ip`.`ip_start`) ASC "
416  "LIMIT %lu, 1",
417  inst->db_name, nasip, s_gid)) {
418  case -1:
419  radlog(L_ERR,
420  "mod_post_auth(): couldn't find "
421  "any more matching host groups");
422  goto end_gid; /* exit the main loop */
423 
424  case 0:
425  fr_connection_release(inst->sql_inst->pool, sqlsock);
426  return RLM_MODULE_FAIL;
427  }
428 
429  /* store the group ID and free memory occupied by results */
430  gid = strtoul(row[0], (char **) NULL, 10);
431  nvp_select_finish(inst, sqlsock);
432 
433  for (s_prio = 0; s_prio < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_prio++) {
434  radlog(L_DBG,
435  "mod_post_auth(): selecting prio on position %lu",
436  s_prio);
437 
438  /* prepare to search for best fit pool */
439  switch (nvp_select(&row, inst, sqlsock,
440  "SELECT "
441  "`ip_pools`.`prio`, "
442  "SUM(`ip_pools`.`weight`) AS `weights_sum`, "
443  "(SUM(`ip_pools`.`total`) - "
444  "SUM(`ip_pools`.`free`)) AS `used_sum` "
445  "FROM "
446  "`%s`.`ip_pools`, "
447  "`%1$s`.`ids`, "
448  "`%1$s`.`pool_names` "
449  "WHERE "
450  "`ip_pools`.`gid` = %lu AND "
451  "`ids`.`id` = `ip_pools`.`pid` AND "
452  "`ids`.`enabled` = 1 AND "
453  "`pool_names`.`pnid` = `ip_pools`.`pnid` AND "
454  "`pool_names`.`name` = '%s' AND "
455  "`ip_pools`.`free` > 0 "
456  "GROUP BY `prio` "
457  "ORDER BY `prio` ASC "
458  "LIMIT %lu, 1",
459  inst->db_name, gid, pname, s_prio)) {
460  case -1:
461  radlog(L_DBG,
462  "mod_post_auth(): couldn't find "
463  "any more matching pools for gid = %lu",
464  gid);
465  goto end_prio; /* select next gid */
466 
467  case 0:
468  fr_connection_release(inst->sql_inst->pool, sqlsock);
469  return RLM_MODULE_FAIL;
470  }
471 
472  /* store the prio and weights sum */
473  prio = strtol(row[0], (char **) NULL, 10);
474  weights_sum = strtoul(row[1], (char **) NULL, 10);
475  used_sum = strtoul(row[2], (char **) NULL, 10);
476 
477  /* free memory */
478  nvp_select_finish(inst, sqlsock);
479 
480  for (s_pid = 0; s_pid < RLM_NETVIM_MAX_ROWS && !(ip.s_addr); s_pid++) {
481  radlog(L_DBG,
482  "mod_post_auth(): selecting PID on position %lu",
483  s_pid);
484 
485  /* search for best fit pool */
486  switch (nvp_select(&row, inst, sqlsock,
487  "SELECT "
488  "`ip_pools`.`pid`, "
489  "`ip_pools`.`ip_start`, "
490  "`ip_pools`.`ip_stop` "
491  "FROM "
492  "`%s`.`ip_pools`, "
493  "`%1$s`.`ids`, "
494  "`%1$s`.`pool_names` "
495  "WHERE "
496  "`ip_pools`.`gid` = %lu AND "
497  "`ids`.`id` = `ip_pools`.`pid` AND "
498  "`ids`.`enabled` = 1 AND "
499  "`pool_names`.`pnid` = `ip_pools`.`pnid` AND "
500  "`pool_names`.`name` = '%s' AND "
501  "`ip_pools`.`free` > 0 AND "
502  "`prio` = %ld "
503  "ORDER BY (`weight`/%lu.0000 - (`total` - `free`)/%lu) DESC "
504  "LIMIT %lu, 1",
505  inst->db_name, gid, pname, prio,
506  weights_sum, used_sum, s_pid)) {
507  case -1:
508  radlog(L_DBG,
509  "mod_post_auth(): couldn't find any more "
510  "matching pools of prio = %ld for gid = %lu",
511  prio, gid);
512  goto end_pid; /* select next prio */
513 
514  case 0:
515  fr_connection_release(inst->sql_inst->pool, sqlsock);
516  return RLM_MODULE_FAIL;
517  }
518 
519  /* store the inst and free memory occupied by results */
520  pid = strtoul(row[0], (char **) NULL, 10);
521  ip_start = strtoul(row[1], (char **) NULL, 10);
522  ip_stop = strtoul(row[2], (char **) NULL, 10);
523  nvp_select_finish(inst, sqlsock);
524 
525  /* reserve an IP address */
526  if (!nvp_query(inst, sqlsock,
527  "UPDATE `%s`.`ips` "
528  "SET "
529  "`pid` = %lu, "
530  "`rsv_since` = NOW(), "
531  "`rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu', "
532  "`rsv_until` = NOW() + INTERVAL %d SECOND "
533  "WHERE "
534  "`ip` BETWEEN %lu AND %lu AND "
535  "("
536  "`pid` IS NULL OR "
537  "(`rsv_until` > 0 AND `rsv_until` < NOW())"
538  ") "
539  "ORDER BY RAND() "
540  "LIMIT 1",
541  inst->db_name, pid, connid, inst->free_after, ip_start, ip_stop)) {
542  fr_connection_release(inst->sql_inst->pool, sqlsock);
543  return RLM_MODULE_FAIL;
544  }
545  else {
546  nvp_finish(inst, sqlsock);
547  }
548 
549  /* select assigned IP address */
550  switch (nvp_select(&row, inst, sqlsock,
551  "SELECT `ip` "
552  "FROM `%s`.`ips` "
553  "WHERE `rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu' "
554  "ORDER BY `rsv_since` DESC "
555  "LIMIT 1",
556  inst->db_name, connid)) {
557  case -1:
558  radlog(L_ERR,
559  "mod_post_auth(): couldn't reserve an IP address "
560  "from pool of pid = %lu (prio = %ld, gid = %lu)",
561  pid, prio, gid);
562  continue; /* select next pid */
563 
564  case 0:
565  fr_connection_release(inst->sql_inst->pool, sqlsock);
566  return RLM_MODULE_FAIL;
567  }
568 
569  /* update free IPs count */
570  if (!nvp_query(inst, sqlsock,
571  "UPDATE `%s`.`ip_pools` "
572  "SET "
573  "`free` = `free` - 1 "
574  "WHERE "
575  "`pid` = %lu "
576  "LIMIT 1",
577  inst->db_name, pid)) {
578  fr_connection_release(inst->sql_inst->pool, sqlsock);
579  return RLM_MODULE_FAIL;
580  }
581  else {
582  nvp_finish(inst, sqlsock);
583  }
584 
585  /* get assigned IP and free memory */
586  ip.s_addr = htonl(strtoul(row[0], (char **) NULL, 10));
587  nvp_select_finish(inst, sqlsock);
588  } /* pid */
589 end_pid: continue; /* stupid */
590  } /* prio */
591 end_prio: continue; /* stupid */
592  } /* gid */
593 end_gid:
594 
595  /* release SQL socket */
596  fr_connection_release(inst->sql_inst->pool, sqlsock);
597 
598  /* no free IP address found */
599  if (!ip.s_addr) {
600  radlog(L_INFO,
601  "mod_post_auth(): no free IP address found!");
602 
603  if (inst->no_free_fail) {
604  radlog(L_DBG, "mod_post_auth(): rejecting user");
605  return RLM_MODULE_REJECT;
606  }
607  else {
608  radlog(L_DBG, "mod_post_auth(): exiting");
609  return RLM_MODULE_NOOP;
610  }
611  }
612 
613  /* add IP address to reply packet */
614  vp = radius_pair_create(request->reply, &request->reply->vps,
615  PW_FRAMED_IP_ADDRESS, 0);
616  vp->vp_ipaddr = ip.s_addr;
617 
618  radlog(L_DBG, "mod_post_auth(): returning %s",
619  inet_ntoa(ip));
620  return RLM_MODULE_OK;
621 }
622 
623 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
624 {
625  VALUE_PAIR *vp;
626  rlm_sql_handle_t *sqlsock;
627  struct in_addr nasip; /* NAS IP */
628  char const *sessid; /* unique session id */
629  char nasipstr[16]; /* NAS IP in string format */
630  uint32_t framedip = 0; /* client's IP, host byte order */
631  uint32_t acct_type;
632 
634 
635  /* if no unique session ID, don't even try */
636  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_ACCT_UNIQUE_SESSION_ID, TAG_ANY);
637  if (vp) {
638  sessid = vp->vp_strvalue;
639  }
640  else {
641  radlog(L_ERR,
642  "mod_accounting(): unique session ID not found");
643  return RLM_MODULE_FAIL;
644  }
645 
646  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_ACCT_STATUS_TYPE, TAG_ANY);
647  if (vp) {
648  acct_type = vp->vp_integer;
649  }
650  else {
651  radlog(L_ERR, "mod_accounting(): "
652  "couldn't find type of accounting packet");
653  return RLM_MODULE_FAIL;
654  }
655 
656  if (!(acct_type == PW_STATUS_START ||
657  acct_type == PW_STATUS_ALIVE ||
658  acct_type == PW_STATUS_STOP ||
659  acct_type == PW_STATUS_ACCOUNTING_OFF ||
660  acct_type == PW_STATUS_ACCOUNTING_ON)) {
661  return RLM_MODULE_NOOP;
662  }
663 
664  /* connect to database */
665  sqlsock = fr_connection_get(inst->sql_inst->pool);
666  if (!sqlsock) {
667  radlog(L_ERR,
668  "mod_accounting(): couldn't connect to database");
669  return RLM_MODULE_FAIL;
670  }
671 
672 
673  switch (acct_type) {
674  case PW_STATUS_START:
675  case PW_STATUS_ALIVE:
676  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_FRAMED_IP_ADDRESS, TAG_ANY);
677  if (!vp) {
678  radlog(L_ERR, "mod_accounting(): no framed IP");
679  fr_connection_release(inst->sql_inst->pool, sqlsock);
680  return RLM_MODULE_FAIL;
681  }
682 
683  framedip = ntohl(vp->vp_ipaddr);
684 
685  if (!nvp_query(inst, sqlsock,
686  "UPDATE `%s`.`ips` "
687  "SET "
688  "`rsv_until` = 0, "
689  "`rsv_by` = '%s' "
690  "WHERE `ip` = %lu",
691  inst->db_name, sessid, framedip)) {
692  fr_connection_release(inst->sql_inst->pool, sqlsock);
693  return RLM_MODULE_FAIL;
694  }
695  nvp_finish(inst, sqlsock);
696  break;
697 
698  case PW_STATUS_STOP:
699  if (!nvp_query(inst, sqlsock,
700  "UPDATE `%s`.`ips`, `%1$s`.`ip_pools` "
701  "SET "
702  "`ips`.`rsv_until` = NOW() + INTERVAL %u SECOND, "
703  "`ip_pools`.`free` = `ip_pools`.`free` + 1 "
704  "WHERE "
705  "`ips`.`rsv_by` = '%s' AND "
706  "`ips`.`ip` BETWEEN `ip_pools`.`ip_start` AND `ip_pools`.`ip_stop`",
707  inst->db_name, inst->free_after, sessid)) {
708  fr_connection_release(inst->sql_inst->pool, sqlsock);
709  return RLM_MODULE_FAIL;
710  }
711  nvp_finish(inst, sqlsock);
712  break;
713 
716  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_NAS_IP_ADDRESS, TAG_ANY);
717  if (!vp) {
718  radlog(L_ERR, "mod_accounting(): no NAS IP");
719  fr_connection_release(inst->sql_inst->pool, sqlsock);
720  return RLM_MODULE_FAIL;
721  }
722 
723  nasip.s_addr = vp->vp_ipaddr;
724  strlcpy(nasipstr, inet_ntoa(nasip), sizeof(nasipstr));
725 
726  if (!nvp_query(inst, sqlsock,
727  "UPDATE `%s`.`ips`, `radacct` "
728  "SET `ips`.`rsv_until` = NOW() + INTERVAL %u SECOND "
729  "WHERE "
730  "`radacct`.`nasipaddress` = '%s' AND "
731  "`ips`.`rsv_by` = `radacct`.`acctuniqueid`",
732  inst->db_name, inst->free_after, nasipstr)) {
733  fr_connection_release(inst->sql_inst->pool, sqlsock);
734  return RLM_MODULE_FAIL;
735  }
736  nvp_finish(inst, sqlsock);
737 
738  break;
739  }
740 
741  fr_connection_release(inst->sql_inst->pool, sqlsock);
742  return RLM_MODULE_OK;
743 }
744 
746 module_t rlm_sqlhpwippool = {
748  .name = "sqlhpwippool",
749  .type = RLM_TYPE_THREAD_SAFE,
750  .inst_size = sizeof(rlm_sqlhpwippool_t),
751  .config = module_config,
752  .instantiate = mod_instantiate,
753  .methods = {
756  },
757 };
PUBLIC int vsnprintf(char *string, size_t length, char *format, va_list args)
Definition: snprintf.c:503
static int nvp_query(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock, char const *fmt,...)
void sql_rcode_t sql_rcode_t rlm_sql_query(rlm_sql_t const *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query) CC_HINT(nonnull(1
Only displayed when debugging is enabled.
Definition: log.h:41
#define DIAG_ON(_x)
Definition: build.h:103
Main server configuration.
Definition: radiusd.h:108
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
Write accounting data to Couchbase documents.
Prototypes and functions for the SQL module.
uint32_t free_after
How many seconds an IP should not be used after freeing.
The module is OK, continue.
Definition: radiusd.h:91
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
module_entry_t * entry
Definition: modpriv.h:67
static int nvp_freeclosed(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) CC_HINT(nonnull)
Metadata exported by the module.
Definition: modules.h:134
#define VENDORPEC_ASN
int radlog(log_type_t lvl, char const *fmt,...) CC_HINT(format(printf
int(* sql_num_rows)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:203
7 methods index for postauth section.
Definition: modules.h:48
#define DIAG_OFF(_x)
Definition: build.h:102
VALUE_PAIR * radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor)
Create a VALUE_PAIR and add it to a list of VALUE_PAIR s.
Definition: pair.c:704
char const * myname
Name of this instance.
#define RLM_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: modules.h:75
bool no_free_fail
Fail if no free IP addresses found.
#define RLM_MODULE_INIT
Definition: modules.h:86
Error message.
Definition: log.h:36
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
sql_rcode_t(* sql_free_result)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:208
static int nvp_syncfree(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
#define PW_STATUS_ALIVE
Definition: radius.h:193
#define PW_STATUS_START
Definition: radius.h:191
#define inst
#define RLM_NETVIM_TMP_PREFIX
module_instance_t * module_instantiate(CONF_SECTION *modules, char const *askedname)
Load a module, and instantiate it.
Definition: modules.c:644
sql_rcode_t(* sql_fetch_row)(rlm_sql_row_t *out, rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:206
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
static int nvp_select(rlm_sql_row_t *row, rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock, char const *fmt,...)
char const * name
Definition: modpriv.h:53
uint32_t sincesync
req. done so far since last free IP sync.
#define pthread_mutex_unlock(_x)
Definition: rlm_eap.h:78
static int nvp_vquery(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock, char const *fmt, va_list ap)
#define ASN_IP_POOL_NAME
rlm_sql_module_t * db
#define RLM_NETVIM_MAX_ROWS
Immediately reject the request.
Definition: radiusd.h:89
uint32_t sync_after
How often to sync with radacct.
static int nvp_select_finish(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
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
module_t rlm_sqlhpwippool
static CONF_PARSER module_config[]
void void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt,...) CC_HINT(format(printf
A truth value.
Definition: radius.h:56
static int mod_instantiate(CONF_SECTION *conf, void *instance)
rlm_sql_config_t * config
Definition: rlm_sql.h:221
32 Bit unsigned integer.
Definition: radius.h:34
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
char const * sql_instance_name
rlm_sql instance to use.
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
Module succeeded without doing anything.
Definition: radiusd.h:96
uint8_t data[]
Definition: eap_pwd.h:625
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
CONF_SECTION * config
Root of the server config.
Definition: radiusd.h:110
void * insthandle
Definition: modpriv.h:68
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
static rlm_rcode_t CC_HINT(nonnull)
static int nvp_cleanup(rlm_sqlhpwippool_t *data)
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
static int nvp_finish(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
#define PW_STATUS_ACCOUNTING_ON
Definition: radius.h:194
#define MAX_QUERY_LEN
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
#define PW_STATUS_STOP
Definition: radius.h:192
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
sql_rcode_t(* sql_finish_query)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:212
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
String of printable characters.
Definition: radius.h:33
Informational message.
Definition: log.h:35
#define RCSID(id)
Definition: build.h:135
rlm_sql_module_t * module
Definition: rlm_sql.h:229
static int r
Definition: rbmonkey.c:66
struct rlm_sqlhpwippool_t rlm_sqlhpwippool_t
fr_connection_pool_t * pool
Definition: rlm_sql.h:220
#define PW_STATUS_ACCOUNTING_OFF
Definition: radius.h:195
sql_rcode_t(* sql_store_result)(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
Definition: rlm_sql.h:200
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
char const * db_name
Netvim database.