📄 rlm_sqlhpwippool.c
字号:
/* * rlm_sqlhpwippool.c * Chooses an IPv4 address from pools defined in ASN Netvim * * Version: $Id: rlm_sqlhpwippool.c,v 1.7 2007/04/20 14:31:31 aland Exp $ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright (c) 2005-2006 Pawel Foremski <pjf@asn.pl>, * 2000-2006 The FreeRADIUS server project * * Current bugs/limits: * - probably works only with newer versions of MySQL (subqueries) * - requires FreeRADIUS' SQL user to have proper permissions on proper tables * from Netvim database * - of course uses dirty hacks to get access to the database * - queries and table names are not configurable * - IPv4 only (I don't even care about IPv6 by now) * - pool names (fetched from database) are not "escaped" * - you have to set encoding of radius.acctuniqueid to same as * netvim.ips.rsv_by */#include "config.h"#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/modpriv.h>#include <ctype.h>#include "rlm_sql.h"#define VENDOR_ASN 23782#define ASN_IP_POOL_NAME 1#define PW_ASN_IP_POOL_NAME (ASN_IP_POOL_NAME | (VENDOR_ASN << 16))#define RLM_NETVIM_LOG_FMT "rlm_sqlhpwippool(%s, line %u): %s"#define RLM_NETVIM_MAX_ROWS 1000000#define RLM_NETVIM_TMP_PREFIX "auth-tmp-"static const char rcsid[] = "$Id: rlm_sqlhpwippool.c,v 1.7 2007/04/20 14:31:31 aland Exp $";typedef struct rlm_sqlhpwippool_t { const char *myname; /* name of this instance */ SQL_INST *sqlinst; /* SQL_INST for requested instance */ rlm_sql_module_t *db; /* here the fun takes place ;-) */#ifdef HAVE_PTHREAD_D pthread_mutex_t mutex; /* used "with" syncafter */#endif int sincesync; /* req. done so far since last free IP sync. */ /* from config */ char *sqlinst_name; /* rlm_sql instance to use */ char *db_name; /* netvim database */ int nofreefail; /* fail if no free IP addresses found */ int freeafter; /* how many seconds an IP should not be used after freeing */ int syncafter; /* how often to sync with radacct */} rlm_sqlhpwippool_t;/* char *name, int type, * size_t offset, void *data, char *dflt */static CONF_PARSER module_config[] = { { "sqlinst_name", PW_TYPE_STRING_PTR, offsetof(rlm_sqlhpwippool_t, sqlinst_name), NULL, "sql" }, { "db_name", PW_TYPE_STRING_PTR, offsetof(rlm_sqlhpwippool_t, db_name), NULL, "netvim" }, { "nofreefail", PW_TYPE_BOOLEAN, offsetof(rlm_sqlhpwippool_t, nofreefail), NULL, "yes" }, { "freeafter", PW_TYPE_INTEGER, offsetof(rlm_sqlhpwippool_t, freeafter), NULL, "300" }, { "syncafter", PW_TYPE_INTEGER, offsetof(rlm_sqlhpwippool_t, syncafter), NULL, "25" }, { NULL, -1, 0, NULL, NULL } /* end */};/* wrapper around radlog which adds prefix with module and instance name */static int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl, const char *fmt, ...){ va_list ap; int r; char pfmt[4096]; /* prefix log message with RLM_NETVIM_LOG_FMT */ snprintf(pfmt, sizeof(pfmt), RLM_NETVIM_LOG_FMT, data->myname, line, fmt); va_start(ap, fmt); r = vradlog(lvl, pfmt, ap); va_end(ap); return r;}/* handy SQL query tool */static int nvp_vquery(unsigned int line, rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock, const char *fmt, va_list ap){ char query[MAX_QUERY_LEN]; vsnprintf(query, MAX_QUERY_LEN, fmt, ap); if (rlm_sql_query(sqlsock, data->sqlinst, query)) { nvp_log(__LINE__, data, L_ERR, "nvp_vquery(): query from line %u: %s", line, (char *)(data->db->sql_error)(sqlsock, data->sqlinst->config)); return 0; } return 1;}/* wrapper around nvp_vquery */static int nvp_query(unsigned int line, rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock, const char *fmt, ...){ int r; va_list ap; va_start(ap, fmt); r = nvp_vquery(line, data, sqlsock, fmt, ap); va_end(ap); return r;}/* handy wrapper around data->db->sql_finish_query() */static int nvp_finish(rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock){ return (data->db->sql_finish_query)(sqlsock, data->sqlinst->config);}/* executes query and fetches first row * -1 on no results * 0 on db error * 1 on success */static int nvp_select(unsigned int line, rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock, const char *fmt, ...){ va_list ap; va_start(ap, fmt); if (!nvp_vquery(line, data, sqlsock, fmt, ap)) { va_end(ap); return 0; } va_end(ap); if ((data->db->sql_store_result)(sqlsock, data->sqlinst->config)) { nvp_log(__LINE__, data, L_ERR, "nvp_select(): error while saving results of query from line %u", line); return 0; } if ((data->db->sql_num_rows)(sqlsock, data->sqlinst->config) < 1) { nvp_log(__LINE__, data, L_DBG, "nvp_select(): no results in query from line %u", line); return -1; } if ((data->db->sql_fetch_row)(sqlsock, data->sqlinst->config)) { nvp_log(__LINE__, data, L_ERR, "nvp_select(): couldn't fetch row " "from results of query from line %u", line); return 0; } return 1;}static int nvp_select_finish(rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock){ return ((data->db->sql_free_result)(sqlsock, data->sqlinst->config) || nvp_finish(data, sqlsock));}/* frees IPs of closed sessions (eg. by external modifications to db) */static int nvp_freeclosed(rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock){ if (!nvp_query(__LINE__, data, sqlsock, "UPDATE `%s`.`ips`, `radacct` " "SET " "`ips`.`rsv_until` = `radacct`.`acctstoptime` + INTERVAL %u SECOND " "WHERE " "`radacct`.`acctstoptime` IS NOT NULL AND " /* session is closed */ "(" /* address is being used */ "`ips`.`pid` IS NOT NULL AND " "(`rsv_until` = 0 OR `rsv_until` > NOW())" ") AND " "`radacct`.`acctuniqueid` = `ips`.`rsv_by`", data->db_name, data->freeafter)) { return 0; } nvp_finish(data, sqlsock); return 1;}/* updates number of free IP addresses in pools */static int nvp_syncfree(rlm_sqlhpwippool_t *data, SQLSOCK *sqlsock){ if (!nvp_query(__LINE__, data, sqlsock, "UPDATE `%s`.`ip_pools` " "SET `ip_pools`.`free` = " "(SELECT COUNT(*) " "FROM `%1$s`.`ips` " "WHERE " "`ips`.`ip` BETWEEN " "`ip_pools`.`ip_start` AND `ip_pools`.`ip_stop` AND " "(" "`ips`.`pid` IS NULL OR " "(`ips`.`rsv_until` > 0 AND `ips`.`rsv_until` < NOW())" "))", data->db_name)) { return 0; } nvp_finish(data, sqlsock); return 1;}/* cleanup IP pools and sync them with radacct */static int nvp_cleanup(rlm_sqlhpwippool_t *data){ SQLSOCK *sqlsock; /* initialize the SQL socket */ sqlsock = sql_get_socket(data->sqlinst); if (!sqlsock) { nvp_log(__LINE__, data, L_ERR, "nvp_cleanup(): error while " "requesting new SQL connection"); return 0; } /* free IPs of closed sessions */ if (!nvp_freeclosed(data, sqlsock)) { sql_release_socket(data->sqlinst, sqlsock); return 0; } /* add sessions opened in the meantime */ if (!nvp_query(__LINE__, data, sqlsock, "UPDATE `%s`.`ips`, `radacct` " "SET " "`ips`.`pid` = 0, " "`ips`.`rsv_by` = `radacct`.`acctuniqueid`, " "`ips`.`rsv_since` = `radacct`.`acctstarttime`, " "`ips`.`rsv_until` = 0 " "WHERE " "`radacct`.`acctstoptime` IS NULL AND " /* session is opened */ "`ips`.`ip` = INET_ATON(`radacct`.`framedipaddress`) AND " "(" "`ips`.`pid` IS NULL OR "/* "(`ips`.`rsv_until` > 0 AND `ips.`rsv_until` < NOW()) " */ "`ips`.`rsv_until` != 0" /* no acct pkt received yet */ ")", data->db_name)) { sql_release_socket(data->sqlinst, sqlsock); return 0; } else { nvp_finish(data, sqlsock); } /* count number of free IP addresses in IP pools */ if (!nvp_syncfree(data, sqlsock)) { sql_release_socket(data->sqlinst, sqlsock); return 0; } sql_release_socket(data->sqlinst, sqlsock); return 1;}static int sqlhpwippool_detach(void *instance){ rlm_sqlhpwippool_t *data = (rlm_sqlhpwippool_t *) instance; /* (*data) is zeroed on instantiation */ if (data->sqlinst_name) free(data->sqlinst_name); if (data->db_name) free(data->db_name); free(data); return 0;}/* standard foobar code */static int sqlhpwippool_instantiate(CONF_SECTION *conf, void **instance){ rlm_sqlhpwippool_t *data; module_instance_t *modinst; /* set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); if (!data) return -1; memset(data, 0, sizeof(*data)); /* so _detach will know what to free */ /* fail if the configuration parameters can't be parsed */ if (cf_section_parse(conf, data, module_config) < 0) { sqlhpwippool_detach(*instance); return -1; } /* save my name */ data->myname = cf_section_name2(conf); if (!data->myname) { data->myname = "(no name)"; } data->sincesync = 0; modinst = find_module_instance(cf_section_find("modules"), (data->sqlinst_name) ); if (!modinst) { nvp_log(__LINE__, data, L_ERR, "sqlhpwippool_instantiate(): cannot find module instance " "named \"%s\"", data->sqlinst_name); return -1; } /* check if the given instance is really a rlm_sql instance */ if (strcmp(modinst->entry->name, "rlm_sql") != 0) { nvp_log(__LINE__, data, L_ERR, "sqlhpwippool_instantiate(): given instance (%s) is not " "an instance of the rlm_sql module", data->sqlinst_name); return -1; } /* save pointers to useful "objects" */ data->sqlinst = (SQL_INST *) modinst->insthandle; data->db = (rlm_sql_module_t *) data->sqlinst->module; /* everything went ok, cleanup pool */ *instance = data; return ((nvp_cleanup(data)) ? 0 : -1);}/* assign new IP address, if required */static int sqlhpwippool_postauth(void *instance, REQUEST *request){ VALUE_PAIR *vp; unsigned char *pname; /* name of requested IP pool */ uint32_t nasip; /* NAS IP in host byte order */ struct in_addr ip = {0}; /* reserved IP for client (net. byte order) */ SQLSOCK *sqlsock; unsigned long s_gid, /* _s_elected in sql result set */ s_prio, /* as above */ s_pid, /* as above */ gid, /* real integer value */ pid, /* as above */ weights_sum, used_sum, ip_start, ip_stop, connid; long prio; rlm_sqlhpwippool_t *data = (rlm_sqlhpwippool_t *) instance; /* if IP is already there, then nothing to do */ vp = pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS); if (vp) { nvp_log(__LINE__, data, L_DBG, "sqlhpwippool_postauth(): IP address " "already in the reply packet - exiting"); return RLM_MODULE_NOOP; } /* if no pool name, we don't need to do anything */ vp = pairfind(request->reply->vps, PW_ASN_IP_POOL_NAME); if (vp) { pname = vp->vp_strvalue; nvp_log(__LINE__, data, L_DBG, "sqlhpwippool_postauth(): pool name = '%s'", pname); } else { nvp_log(__LINE__, data, L_DBG, "sqlhpwippool_postauth(): no IP pool name - exiting"); return RLM_MODULE_NOOP; } /* if no NAS IP address, assign 0 */ vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS); if (vp) { nasip = ntohl(vp->vp_ipaddr); } else { nasip = 0; nvp_log(__LINE__, data, L_DBG, "sqlhpwippool_postauth(): no NAS IP address in " "the request packet - using \"0.0.0.0/0\" (any)"); } /* get our database connection */ sqlsock = sql_get_socket(data->sqlinst);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -