/*
* 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 64
static 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)
#endif
typedef 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);
DEBUG("rlm_ippool: Entry not found");
}
return RLM_MODULE_OK;
}
static int ippool_postauth(void *instance, REQUEST *request)
{
rlm_ippool_t *data = (rlm_ippool_t *) instance;
unsigned int port = 0;
int delete = 0;
int found = 0;
int mppp = 0;
int extra = 0;
int rcode;
int num = 0;
char nas[MAX_NAS_NAME_SIZE];
datum key_datum;
datum nextkey;
datum data_datum;
datum save_datum;
ippool_key key;
ippool_info entry;
VALUE_PAIR *vp;
char *cli = NULL;
char str[32];
/* quiet the compiler */
instance = instance;
request = request;
/* Check if Pool-Name attribute exists. If it exists check our name and
* run only if they match
*/
if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
if (data->name == NULL || strcmp(data->name,vp->strvalue))
return RLM_MODULE_NOOP;
} else {
DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
return RLM_MODULE_NOOP;
}
/*
* Get the nas ip address
* If not fail
*/
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. Return NOOP.");
return RLM_MODULE_NOOP;
}
}
/*
* Find the caller id
*/
if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
cli = vp->strvalue;
/*
* Find the port
* If not fail
*/
if ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
port = vp->lvalue;
else{
DEBUG("rlm_ippool: Could not find nas port information. 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 there is a corresponding entry in the database with active=1 it is stale.
* Set active to zero
*/
found = 1;
memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
free(data_datum.dptr);
if (entry.active){
DEBUG("rlm_ippool: Found a stale 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);
/*
* If there is a Framed-IP-Address attribute in the reply, check for override
*/
if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
DEBUG("rlm_ippool: Found Framed-IP-Address attribute in reply attribute list.");
if (data->override)
{
/* Override supplied Framed-IP-Address */
DEBUG("rlm_ippool: override is set to yes. Override the existing Framed-IP-Address attribute.");
pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
} else {
/* Abort */
DEBUG("rlm_ippool: override is set to no. Return NOOP.");
return RLM_MODULE_NOOP;
}
}
/*
* Walk through the database searching for an active=0 entry.
* We search twice. Once to see if we have an active entry with the same callerid
* so that MPPP can work ok and then once again to find a free entry.
*/
pthread_mutex_lock(&data->op_mutex);
key_datum.dptr = NULL;
if (cli != NULL){
key_datum = gdbm_firstkey(data->gdbm);
while(key_datum.dptr){
data_datum = gdbm_fetch(data->gdbm, key_datum);
if (data_datum.dptr){
memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
free(data_datum.dptr);
/*
* If we find an entry for the same caller-id and nas with active=1
* then we use that for multilink (MPPP) to work properly.
*/
if (strcmp(entry.cli,cli) == 0 && entry.active){
memcpy(&key,key_datum.dptr,sizeof(ippool_key));
if (!strcmp(key.nas,nas)){
mppp = 1;
break;
}
}
}
nextkey = gdbm_nextkey(data->gdbm, key_datum);
free(key_datum.dptr);
key_datum = nextkey;
}
}
if (key_datum.dptr == NULL){
key_datum = gdbm_firstkey(data->gdbm);
while(key_datum.dptr){
data_datum = gdbm_fetch(data->gdbm, key_datum);
if (data_datum.dptr){
memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
free(data_datum.dptr);
/*
* Find an entry with active == 0
* or an entry that has expired
*/
if (entry.active == 0 || (entry.timestamp && ((entry.timeout &&
request->timestamp >= (entry.timestamp + entry.timeout)) ||
(data->max_timeout && request->timestamp >= (entry.timestamp + data->max_timeout))))){
datum tmp;
tmp.dptr = (char *) &entry.ipaddr;
tmp.dsize = sizeof(uint32_t);
data_datum = gdbm_fetch(data->ip, tmp);
/*
* If we find an entry in the ip index and the number is zero (meaning
* that we haven't allocated the same ip address to another nas/port pair)
* or if we don't find an entry then delete the session entry so
* that we can change the key (nas/port)
* Else we don't delete the session entry since we haven't yet deallocated the
* corresponding ip address and we continue our search.
*/
if (data_datum.dptr){
memcpy(&num,data_datum.dptr, sizeof(int));
free(data_datum.dptr);
if (num == 0){
delete = 1;
break;
}
}
else{
delete = 1;
break;
}
}
}
nextkey = gdbm_nextkey(data->gdbm, key_datum);
free(key_datum.dptr);
key_datum = nextkey;
}
}
/*
* If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
* the reply
* We keep the operation mutex locked until after we have set the corresponding entry active
*/
if (key_datum.dptr){
if (found && !mppp){
/*
* Found == 1 means we have the nas/port combination entry in our database
* We exchange the ip address between the nas/port entry and the free entry
* Afterwards we will save the free ip address to the nas/port entry.
* That is:
* ---------------------------------------------
* - NAS/PORT Entry |||| Free Entry ||| Time
* - IP1 IP2(Free) BEFORE
* - IP2(Free) IP1 AFTER
* ---------------------------------------------
*
* We only do this if we are NOT doing MPPP
*
*/
datum key_datum_tmp;
datum data_datum_tmp;
ippool_key key_tmp;
memset(key_tmp.nas,0,MAX_NAS_NAME_SIZE);
strncpy(key_tmp.nas,nas,MAX_NAS_NAME_SIZE -1 );
key_tmp.port = port;
DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key_tmp.nas,key_tmp.port);
key_datum_tmp.dptr = (char *) &key_tmp;
key_datum_tmp.dsize = sizeof(ippool_key);
data_datum_tmp = gdbm_fetch(data->gdbm, key_datum_tmp);
if (data_datum_tmp.dptr != NULL){
rcode = gdbm_store(data->gdbm, key_datum, data_datum_tmp, 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;
}
free(data_datum_tmp.dptr);
}
}
else{
/*
* We have not found the nas/port combination
*/
if (delete){
/*
* Delete the entry so that we can change the key
* All is well. We delete one entry and we add one entry
*/
gdbm_delete(data->gdbm, key_datum);
}
else{
/*
* We are doing MPPP. (mppp should be 1)
* We don't do anything.
* We will create an extra not needed entry in the database in this case
* but we don't really care since we always also use the ip_index database
* when we search for a free entry.
* We will also delete that entry on the accounting section so that we only
* have one nas/port entry referencing each ip
*/
if (mppp)
extra = 1;
if (!mppp)
radlog(L_ERR, "rlm_ippool: mppp is not one. Please report this behaviour.");
}
}
free(key_datum.dptr);
entry.active = 1;
entry.timestamp = request->timestamp;
if ((vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL)
entry.timeout = (time_t) vp->lvalue;
else
entry.timeout = 0;
if (extra)
entry.extra = 1;
data_datum.dptr = (char *) &entry;
data_datum.dsize = sizeof(ippool_info);
memset(key.nas,0,MAX_NAS_NAME_SIZE);
strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
key.port = port;
key_datum.dptr = (char *) &key;
key_datum.dsize = sizeof(ippool_key);
DEBUG2("rlm_ippool: Allocating ip to nas/port: %s/%u",key.nas,key.port);
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;
}
/* Increase the ip index count */
key_datum.dptr = (char *) &entry.ipaddr;
key_datum.dsize = sizeof(uint32_t);
data_datum = gdbm_fetch(data->ip, key_datum);
if (data_datum.dptr){
memcpy(&num,data_datum.dptr,sizeof(int));
free(data_datum.dptr);
} else
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;
}
pthread_mutex_unlock(&data->op_mutex);
DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %u",ip_ntoa(str,entry.ipaddr),
key.nas,port);
if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
radlog(L_ERR|L_CONS, "no memory");
return RLM_MODULE_FAIL;
}
vp->lvalue = entry.ipaddr;
ip_ntoa(vp->strvalue, vp->lvalue);
pairadd(&request->reply->vps, vp);
/*
* If there is no Framed-Netmask attribute in the
* reply, add one
*/
if (pairfind(request->reply->vps, PW_FRAMED_IP_NETMASK) == NULL) {
if ((vp = paircreate(PW_FRAMED_IP_NETMASK, PW_TYPE_IPADDR)) == NULL)
radlog(L_ERR|L_CONS, "no memory");
else {
vp->lvalue = ntohl(data->netmask);
ip_ntoa(vp->strvalue, vp->lvalue);
pairadd(&request->reply->vps, vp);
}
}
}
else{
pthread_mutex_unlock(&data->op_mutex);
DEBUG("rlm_ippool: No available ip addresses in pool.");
return RLM_MODULE_NOTFOUND;
}
return RLM_MODULE_OK;
}
static int ippool_detach(void *instance)
{
rlm_ippool_t *data = (rlm_ippool_t *) instance;
gdbm_close(data->gdbm);
gdbm_close(data->ip);
free(data->session_db);
free(data->ip_index);
pthread_mutex_destroy(&data->op_mutex);
free(instance);
return 0;
}
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
*
* If the module needs to temporarily modify it's instantiation
* data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
* The server will then take care of ensuring that the module
* is single-threaded.
*/
module_t rlm_ippool = {
"IPPOOL",
RLM_TYPE_THREAD_SAFE, /* type */
NULL, /* initialization */
ippool_instantiate, /* instantiation */
{
NULL, /* authentication */
NULL, /* authorization */
NULL, /* preaccounting */
ippool_accounting, /* accounting */
NULL, /* checksimul */
NULL, /* pre-proxy */
NULL, /* post-proxy */
ippool_postauth /* post-auth */
},
ippool_detach, /* detach */
NULL, /* destroy */
};
syntax highlighted by Code2HTML, v. 0.9.1