📄 rlm_ippool.c
字号:
/* * rlm_ippool.c * * Version: $Id: rlm_ippool.c,v 1.31 2004/05/03 10:51:26 kkalev 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 2001 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. */#include "config.h"#include "autoconf.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#ifdef HAVE_SYS_TYPES_H#include <sys/types.h>#endif#ifdef HAVE_STDINT_H#include <stdint.h>#endif#ifdef HAVE_INTTYPES_H#include <inttypes.h>#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#include "radiusd.h"#include "modules.h"#include "conffile.h"#include <gdbm.h>#include <time.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 64static const char rcsid[] = "$Id: rlm_ippool.c,v 1.31 2004/05/03 10:51:26 kkalev Exp $";/* * 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; 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 nas[MAX_NAS_NAME_SIZE]; unsigned int port;} 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 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 }, { "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; int i; unsigned j; const char *cli = "0"; 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 or_result; char str[32]; const char *nas_init = "NOT_EXIST"; 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; } strcpy(key.nas, nas_init); key.port = j; 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)); free(data); gdbm_close(data->gdbm); gdbm_close(data->ip); 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; unsigned int port = ~0; int rcode; char nas[MAX_NAS_NAME_SIZE]; ippool_info entry; ippool_key key; int num = 0; VALUE_PAIR *vp; char str[32]; if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) acctstatustype = vp->lvalue; 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 ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL) port = vp->lvalue; else { DEBUG("rlm_ippool: Could not find port number in packet. Return NOOP."); return RLM_MODULE_NOOP; } if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL) strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1); else { if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL) strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1); else { DEBUG("rlm_ippool: Could not find nas information in packet. Return NOOP."); return RLM_MODULE_NOOP; } } 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; } memset(key.nas,0,MAX_NAS_NAME_SIZE); strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 ); key.port = port; DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key.nas,key.port); 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/port: %s/%u",ip_ntoa(str,entry.ipaddr),port); 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); } } } pthread_mutex_unlock(&data->op_mutex); } else{ pthread_mutex_unlock(&data->op_mutex);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -