📄 rlm_ippool.c
字号:
/* * rlm_ippool.c * * Version: $Id: rlm_ippool.c,v 1.51 2008/01/06 17:27:41 nbk 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001,2006 The FreeRADIUS server project * Copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr> * * March 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Initial release * April 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add support for the Pool-Name attribute * May 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Check the return value of a gdbm_fetch() we didn't check * - Change the nas entry in the ippool_key structure from uint32 to string[64] * That should allow us to also use the NAS-Identifier attribute * Sep 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Move from authorize to post-auth * - Use mutex locks when accessing the gdbm files * - Fail if we don't find nas port information * Oct 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Do a memset(0) on the key.nas before doing searches. Nusty bug * Jul 2003, Kostas Kalevras <kkalev@noc.ntua.gr> * - Make Multilink work this time * - Instead of locking file operations, lock transactions. That means we only keep * one big transaction lock instead of per file locks (mutexes). * Sep 2003, Kostas Kalevras <kkalev@noc.ntua.gr> * - Fix postauth to not leak ip's * Add an extra attribute in each entry <char extra> signifying if we need to delete this * entry in the accounting phase. This is only true in case we are doing MPPP * Various other code changes. Code comments should explain things * Highly experimental at this phase. * Mar 2004, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add a timestamp and a timeout attribute in ippool_info. When we assign an ip we set timestamp * to request->timestamp and timeout to %{Session-Timeout:-0}. When we search for a free entry * we check if timeout has expired. If it has then we free the entry. We also add a maximum * timeout configuration directive. If it is non zero then we also use that one to free entries. * Jul 2004, Kostas Kalevras <kkalev@noc.ntua.gr> * - If Pool-Name is set to DEFAULT then always run. * Mar 2005, Kostas Kalevras <kkalev@noc.ntua.gr> * - Make the key an MD5 of a configurable xlated string. This closes Bug #42 */#include <freeradius-devel/ident.h>RCSID("$Id: rlm_ippool.c,v 1.51 2008/01/06 17:27:41 nbk Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include "config.h"#include <ctype.h>#include "../../include/md5.h"#include <gdbm.h>#ifdef NEEDS_GDBM_SYNC# define GDBM_SYNCOPT GDBM_SYNC#else# define GDBM_SYNCOPT 0#endif#ifdef GDBM_NOLOCK#define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)#else#define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)#endif#define MAX_NAS_NAME_SIZE 64/* * Define a structure for our module configuration. * * These variables do not need to be in a structure, but it's * a lot cleaner to do so, and a pointer to the structure can * be used as the instance handle. */typedef struct rlm_ippool_t { char *session_db; char *ip_index; char *name; char *key; uint32_t range_start; uint32_t range_stop; uint32_t netmask; time_t max_timeout; int cache_size; int override; GDBM_FILE gdbm; GDBM_FILE ip;#ifdef HAVE_PTHREAD_H pthread_mutex_t op_mutex;#endif} rlm_ippool_t;#ifndef HAVE_PTHREAD_H/* * This is easier than ifdef's throughout the code. */#define pthread_mutex_init(_x, _y)#define pthread_mutex_destroy(_x)#define pthread_mutex_lock(_x)#define pthread_mutex_unlock(_x)#endiftypedef struct ippool_info { uint32_t ipaddr; char active; char cli[32]; char extra; time_t timestamp; time_t timeout;} ippool_info;typedef struct ippool_key { char key[16];} ippool_key;/* * A mapping of configuration file names to internal variables. * * Note that the string is dynamically allocated, so it MUST * be freed. When the configuration file parse re-reads the string, * it free's the old one, and strdup's the new one, placing the pointer * to the strdup'd string into 'config.string'. This gets around * buffer over-flows. */static const CONF_PARSER module_config[] = { { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL }, { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL }, { "key", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,key), NULL, "%{NAS-IP-Address} %{NAS-Port}" }, { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" }, { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" }, { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" }, { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" }, { "override", PW_TYPE_BOOLEAN, offsetof(rlm_ippool_t,override), NULL, "no" }, { "maximum-timeout", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,max_timeout), NULL, "0" }, { NULL, -1, 0, NULL, NULL }};/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */static int ippool_instantiate(CONF_SECTION *conf, void **instance){ rlm_ippool_t *data; int cache_size; ippool_info entry; ippool_key key; datum key_datum; datum data_datum; const char *cli = "0"; const char *pool_name = NULL; /* * Set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); if (!data) { return -1; } memset(data, 0, sizeof(*data)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } cache_size = data->cache_size; if (data->session_db == NULL) { radlog(L_ERR, "rlm_ippool: 'session-db' must be set."); free(data); return -1; } if (data->ip_index == NULL) { radlog(L_ERR, "rlm_ippool: 'ip-index' must be set."); free(data); return -1; } data->range_start = htonl(data->range_start); data->range_stop = htonl(data->range_stop); data->netmask = htonl(data->netmask); if (data->range_start == 0 || data->range_stop == 0 || \ data->range_start >= data->range_stop ) { radlog(L_ERR, "rlm_ippool: Invalid configuration data given."); free(data); return -1; } data->gdbm = gdbm_open(data->session_db, sizeof(int), GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL); if (data->gdbm == NULL) { radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s", data->session_db, strerror(errno)); return -1; } data->ip = gdbm_open(data->ip_index, sizeof(int), GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL); if (data->ip == NULL) { radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s", data->ip_index, strerror(errno)); return -1; } if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) radlog(L_ERR, "rlm_ippool: Failed to set cache size"); if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) radlog(L_ERR, "rlm_ippool: Failed to set cache size"); key_datum = gdbm_firstkey(data->gdbm); if (key_datum.dptr == NULL){ /* * If the database does not exist initialize it. * We set the nas/port pairs to not existent values and * active = 0 */ int rcode; uint32_t i, j; uint32_t or_result; char str[32]; char init_str[17]; DEBUG("rlm_ippool: Initializing database"); for(i=data->range_start,j=~0;i<=data->range_stop;i++,j--){ /* * Net and Broadcast addresses are excluded */ or_result = i | data->netmask; if (~data->netmask != 0 && (or_result == data->netmask || (~or_result == 0))) { DEBUG("rlm_ippool: IP %s excluded", ip_ntoa(str, ntohl(i))); continue; } sprintf(init_str,"%016d",j); DEBUG("rlm_ippool: Initialized bucket: %s",init_str); memcpy(key.key, init_str,16); key_datum.dptr = (char *) &key; key_datum.dsize = sizeof(ippool_key); entry.ipaddr = ntohl(i); entry.active = 0; entry.extra = 0; entry.timestamp = 0; entry.timeout = 0; strcpy(entry.cli,cli); data_datum.dptr = (char *) &entry; data_datum.dsize = sizeof(ippool_info); rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE); if (rcode < 0) { radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s", data->session_db, gdbm_strerror(gdbm_errno)); gdbm_close(data->gdbm); gdbm_close(data->ip); free(data); return -1; } } } else free(key_datum.dptr); /* Add the ip pool name */ data->name = NULL; pool_name = cf_section_name2(conf); if (pool_name != NULL) data->name = strdup(pool_name); pthread_mutex_init(&data->op_mutex, NULL); *instance = data; return 0;}/* * Check for an Accounting-Stop * If we find one and we have allocated an IP to this nas/port combination, deallocate it. */static int ippool_accounting(void *instance, REQUEST *request){ rlm_ippool_t *data = (rlm_ippool_t *)instance; datum key_datum; datum data_datum; datum save_datum; int acctstatustype = 0; int rcode; ippool_info entry; ippool_key key; int num = 0; VALUE_PAIR *vp; char str[32]; uint8_t key_str[17]; char hex_str[35]; char xlat_str[MAX_STRING_LEN]; FR_MD5_CTX md5_context; if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) acctstatustype = vp->vp_integer; else { DEBUG("rlm_ippool: Could not find account status type in packet. Return NOOP."); return RLM_MODULE_NOOP; } switch(acctstatustype){ case PW_STATUS_STOP: if (!radius_xlat(xlat_str,MAX_STRING_LEN,data->key, request, NULL)){ DEBUG("rlm_ippool: xlat on the 'key' directive failed"); return RLM_MODULE_NOOP; } fr_MD5Init(&md5_context); fr_MD5Update(&md5_context, xlat_str, strlen(xlat_str)); fr_MD5Final(key_str, &md5_context); key_str[16] = '\0'; fr_bin2hex(key_str,hex_str,16); hex_str[32] = '\0'; DEBUG("rlm_ippool: MD5 on 'key' directive maps to: %s",hex_str); memcpy(key.key,key_str,16); break; default: /* We don't care about any other accounting packet */ DEBUG("rlm_ippool: This is not an Accounting-Stop. Return NOOP."); return RLM_MODULE_NOOP; } DEBUG("rlm_ippool: Searching for an entry for key: '%s'",xlat_str); key_datum.dptr = (char *) &key; key_datum.dsize = sizeof(ippool_key); pthread_mutex_lock(&data->op_mutex); data_datum = gdbm_fetch(data->gdbm, key_datum); if (data_datum.dptr != NULL){ /* * If the entry was found set active to zero */ memcpy(&entry, data_datum.dptr, sizeof(ippool_info)); free(data_datum.dptr); DEBUG("rlm_ippool: Deallocated entry for ip: %s",ip_ntoa(str,entry.ipaddr)); entry.active = 0; entry.timestamp = 0; entry.timeout = 0; /* * Save the reference to the entry */ save_datum.dptr = key_datum.dptr; save_datum.dsize = key_datum.dsize; data_datum.dptr = (char *) &entry; data_datum.dsize = sizeof(ippool_info); rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE); if (rcode < 0) { radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s", data->session_db, gdbm_strerror(gdbm_errno)); pthread_mutex_unlock(&data->op_mutex); return RLM_MODULE_FAIL; } /* * Decrease allocated count from the ip index */ key_datum.dptr = (char *) &entry.ipaddr; key_datum.dsize = sizeof(uint32_t); data_datum = gdbm_fetch(data->ip, key_datum); if (data_datum.dptr != NULL){ memcpy(&num, data_datum.dptr, sizeof(int)); free(data_datum.dptr); if (num >0){ num--; DEBUG("rlm_ippool: num: %d",num); data_datum.dptr = (char *) # data_datum.dsize = sizeof(int); rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE); if (rcode < 0) { radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s", data->ip_index, gdbm_strerror(gdbm_errno)); pthread_mutex_unlock(&data->op_mutex); return RLM_MODULE_FAIL; } if (num >0 && entry.extra == 1){ /* * We are doing MPPP and we still have nas/port entries referencing * this ip. Delete this entry so that eventually we only keep one * reference to this ip. */ gdbm_delete(data->gdbm,save_datum); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -