diff options
Diffstat (limited to 'src/modules/rlm_ippool/rlm_ippool.c')
-rw-r--r-- | src/modules/rlm_ippool/rlm_ippool.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/src/modules/rlm_ippool/rlm_ippool.c b/src/modules/rlm_ippool/rlm_ippool.c new file mode 100644 index 0000000..7762b15 --- /dev/null +++ b/src/modules/rlm_ippool/rlm_ippool.c @@ -0,0 +1,832 @@ +/* + * This program is 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 + */ + +/** + * $Id$ + * @file rlm_ippool.c + * @brief Allocates an IPv4 address from a pool stored in a GDBM database. + * + * @copyright 2000,2006 The FreeRADIUS server project + * @copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr> + */ +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> +#include <freeradius-devel/rad_assert.h> + +#include "config.h" +#include <ctype.h> + +#ifdef WITH_DHCP +#include <freeradius-devel/dhcp.h> +#endif + +#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 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 const *filename; + char const *ip_index; + char const *name; + char const *key; + + fr_ipaddr_t range_start_addr; + fr_ipaddr_t range_stop_addr; + fr_ipaddr_t netmask_addr; + uint32_t range_start; + uint32_t range_stop; + uint32_t netmask; + + uint32_t max_timeout; + uint32_t cache_size; + bool 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 key[16]; +} ippool_key; + +static const CONF_PARSER module_config[] = { + { "session-db", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, rlm_ippool_t, filename), NULL }, + { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_ippool_t, filename), NULL }, + + { "ip-index", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_ippool_t, ip_index), NULL }, + { "ip_index", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_ippool_t, ip_index), NULL }, + + { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_ippool_t, key), "%{NAS-IP-Address} %{NAS-Port}" }, + + { "range-start", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR | PW_TYPE_DEPRECATED, rlm_ippool_t, range_start_addr), NULL }, + { "range_start", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, range_start_addr), "0" }, + + { "range-stop", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR | PW_TYPE_DEPRECATED, rlm_ippool_t, range_stop_addr), NULL }, + { "range_stop", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, range_stop_addr), "0" }, + + { "netmask", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, netmask_addr), "0" }, + + { "cache-size", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_ippool_t, cache_size), NULL }, + { "cache_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ippool_t, cache_size), "1000" }, + + { "override", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ippool_t, override), "no" }, + + { "maximum-timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_ippool_t, max_timeout), NULL }, + { "maximum_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ippool_t, max_timeout), "0" }, + CONF_PARSER_TERMINATOR +}; + +/* + * 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 mod_instantiate(CONF_SECTION *conf, void *instance) +{ + rlm_ippool_t *inst = instance; + int cache_size; + ippool_info entry; + ippool_key key; + datum key_datum; + datum data_datum; + + char const *cli = "0"; + char const *pool_name = NULL; + + int rcode; + uint32_t i, j; + uint32_t or_result; + char str[32]; + char init_str[17]; + + /* + * Add the ip pool name + */ + inst->name = NULL; + pool_name = cf_section_name2(conf); + if (pool_name != NULL) { + inst->name = talloc_typed_strdup(inst, pool_name); + } + + cache_size = inst->cache_size; + + rad_assert(inst->filename && *inst->filename); + rad_assert(inst->ip_index && *inst->ip_index); + + inst->range_start = htonl(*((uint32_t *)(&(inst->range_start_addr.ipaddr.ip4addr)))); + inst->range_stop = htonl(*((uint32_t *)(&(inst->range_stop_addr.ipaddr.ip4addr)))); + inst->netmask = htonl(*((uint32_t *)(&(inst->netmask_addr.ipaddr.ip4addr)))); + if (inst->range_start == 0 || inst->range_stop == 0 || \ + inst->range_start >= inst->range_stop ) { + cf_log_err_cs(conf, "Invalid data range"); + return -1; + } + + { + char *file; + + memcpy(&file, &inst->filename, sizeof(file)); + inst->gdbm = gdbm_open(file, sizeof(int), + GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL); + } + + if (!inst->gdbm) { + ERROR("rlm_ippool: Failed to open file %s: %s", inst->filename, fr_syserror(errno)); + + return -1; + } + + { + char *file; + + memcpy(&file, &inst->ip_index, sizeof(file)); + inst->ip = gdbm_open(file, sizeof(int), + GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL); + } + + if (!inst->ip) { + ERROR("rlm_ippool: Failed to open file %s: %s", inst->ip_index, fr_syserror(errno)); + + return -1; + } + + if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) { + ERROR("rlm_ippool: Failed to set cache size"); + } + + if (gdbm_setopt(inst->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) { + ERROR("rlm_ippool: Failed to set cache size"); + } + + pthread_mutex_init(&inst->op_mutex, NULL); + + key_datum = gdbm_firstkey(inst->gdbm); + if (key_datum.dptr) { + free(key_datum.dptr); + return 0; + } + + /* + * If the database does not exist initialize it. + * We set the nas/port pairs to not existent values and + * active = 0 + */ + DEBUG("rlm_ippool: Initializing database"); + for (i = inst->range_start, j=~0; i <= inst->range_stop; i++, j--){ + /* + * Net and Broadcast addresses are excluded + */ + or_result = i | inst->netmask; + if (~inst->netmask != 0 && (or_result == inst->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(inst->gdbm, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + ERROR("rlm_ippool: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno)); + gdbm_close(inst->gdbm); + gdbm_close(inst->ip); + return -1; + } + } + + return 0; +} + +/** Decrease allocated count from the ip index + * + */ +static int decrease_allocated_count(rlm_ippool_t *inst, REQUEST *request, ippool_info *entry, datum *save_datum) +{ + datum data_datum; + datum key_datum; + int num; + + + key_datum.dptr = (char *) &(entry->ipaddr); + key_datum.dsize = sizeof(uint32_t); + data_datum = gdbm_fetch(inst->ip, key_datum); + if (!data_datum.dptr) { + return 0; + } + memcpy(&num, data_datum.dptr, sizeof(int)); + free(data_datum.dptr); + if (num > 0){ + int rcode; + + num--; + + RDEBUG("Allocated count now: %i", num); + data_datum.dptr = (char *) # + data_datum.dsize = sizeof(int); + rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + RDEBUG("Failed storing data to %s: %s", inst->ip_index, gdbm_strerror(gdbm_errno)); + return -1; + } + 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(inst->gdbm, *save_datum); + } + } + + 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 rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request) +{ + rlm_ippool_t *inst = instance; + + datum key_datum; + ippool_key key; + datum data_datum; + ippool_info entry; + datum save_datum; + + int rcode; + VALUE_PAIR *vp; + + char str[32]; + uint8_t key_str[17]; + char hex_str[35]; + char xlat_str[MAX_STRING_LEN]; + int ret; + + vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY); + if (!vp) { + RDEBUG2("Could not find account status type in packet"); + return RLM_MODULE_INVALID; + } + + switch (vp->vp_integer) { + case PW_STATUS_STOP: + { + FR_MD5_CTX md5_context; + if (radius_xlat(xlat_str, sizeof(xlat_str), request, inst->key, NULL, NULL) < 0){ + return RLM_MODULE_FAIL; + } + + fr_md5_init(&md5_context); + fr_md5_update(&md5_context, (uint8_t *)xlat_str, strlen(xlat_str)); + fr_md5_final(key_str, &md5_context); + + key_str[16] = '\0'; + fr_bin2hex(hex_str, key_str, 16); + hex_str[32] = '\0'; + + RDEBUG2("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 */ + RDEBUG2("This is not an Accounting-Stop"); + + return RLM_MODULE_NOOP; + } + + RDEBUG2("Searching for an entry for key: '%s'", xlat_str); + key_datum.dptr = (char *) &key; + key_datum.dsize = sizeof(ippool_key); + + pthread_mutex_lock(&inst->op_mutex); + data_datum = gdbm_fetch(inst->gdbm, key_datum); + if (data_datum.dptr == NULL) { + pthread_mutex_unlock(&inst->op_mutex); + RDEBUG2("Entry not found"); + + return RLM_MODULE_NOTFOUND; + } + + /* + * If the entry was found set active to zero + */ + memcpy(&entry, data_datum.dptr, sizeof(ippool_info)); + free(data_datum.dptr); + + RDEBUG("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(inst->gdbm, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + pthread_mutex_unlock(&inst->op_mutex); + REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno)); + + return RLM_MODULE_FAIL; + } + + /* + * Decrease allocated count from the ip index + */ + ret = decrease_allocated_count(inst, request, &entry, &save_datum); + pthread_mutex_unlock(&inst->op_mutex); + if (ret < 0) { + return RLM_MODULE_FAIL; + } + + return RLM_MODULE_OK; +} + +static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request) +{ + rlm_ippool_t *inst = instance; + + datum key_datum; + ippool_key key; + datum nextkey; + datum data_datum; + ippool_info entry; + datum save_datum; + + int delete = 0; + bool found = false; + int mppp = 0; + int extra = 0; + int rcode; + int num = 0; + + VALUE_PAIR *vp; + char const *cli = NULL; + char str[32]; + uint8_t key_str[17]; + char hex_str[35]; + char xlat_str[MAX_STRING_LEN]; + FR_MD5_CTX md5_context; + +#ifdef WITH_DHCP + bool dhcp = false; +#endif + int attr_ipaddr = PW_FRAMED_IP_ADDRESS; + int attr_ipmask = PW_FRAMED_IP_NETMASK; + int vendor_ipaddr = 0; + + /* + * Check if Pool-Name attribute exists. If it exists check our name and + * run only if they match + */ + vp = fr_pair_find_by_num(request->config, PW_POOL_NAME, 0, TAG_ANY); + if (vp != NULL){ + if (!inst->name || (strcmp(inst->name,vp->vp_strvalue) && strcmp(vp->vp_strvalue,"DEFAULT"))) + return RLM_MODULE_NOOP; + } else { + RDEBUG("Could not find Pool-Name attribute"); + return RLM_MODULE_NOOP; + } + + /* + * Find the caller id + */ + vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY); + if (vp != NULL) { + cli = vp->vp_strvalue; + } + +#ifdef WITH_DHCP + if (request->listener->type == RAD_LISTEN_DHCP) { + dhcp = 1; + attr_ipaddr = PW_DHCP_YOUR_IP_ADDRESS; + vendor_ipaddr = DHCP_MAGIC_VENDOR; + attr_ipmask = PW_DHCP_SUBNET_MASK; + } +#endif + + if (radius_xlat(xlat_str, sizeof(xlat_str), request, inst->key, NULL, NULL) < 0){ + return RLM_MODULE_FAIL; + } + + fr_md5_init(&md5_context); + fr_md5_update(&md5_context, (uint8_t *)xlat_str, strlen(xlat_str)); + fr_md5_final(key_str, &md5_context); + key_str[16] = '\0'; + fr_bin2hex(hex_str, key_str, 16); + hex_str[32] = '\0'; + + RDEBUG("MD5 on 'key' directive maps to: %s", hex_str); + memcpy(key.key, key_str, 16); + + RDEBUG("Searching for an entry for key: '%s'", hex_str); + key_datum.dptr = (char *) &key; + key_datum.dsize = sizeof(ippool_key); + + pthread_mutex_lock(&inst->op_mutex); + data_datum = gdbm_fetch(inst->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 = true; + memcpy(&entry, data_datum.dptr, sizeof(ippool_info)); + free(data_datum.dptr); + + if (entry.active){ + int ret; + RDEBUG("Found a stale 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(inst->gdbm, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno)); + pthread_mutex_unlock(&inst->op_mutex); + return RLM_MODULE_FAIL; + } + + /* + * Decrease allocated count for the ip + */ + ret = decrease_allocated_count(inst, request, &entry, &save_datum); + pthread_mutex_unlock(&inst->op_mutex); + if (ret < 0) { + return RLM_MODULE_FAIL; + } + } + } + + pthread_mutex_unlock(&inst->op_mutex); + + /* + * If there is a Framed-IP-Address (or Dhcp-Your-IP-Address) + * attribute in the reply, check for override + */ + if (fr_pair_find_by_num(request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY) != NULL) { + RDEBUG("Found IP address attribute in reply attribute list"); + if (!inst->override) { + RDEBUG("override is set to no. Return NOOP"); + return RLM_MODULE_NOOP; + } + + RDEBUG("Override supplied IP address"); + fr_pair_delete_by_num(&request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY); + } + + /* + * 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 caller_id + * so that MPPP can work ok and then once again to find a free entry. + */ + pthread_mutex_lock(&inst->op_mutex); + key_datum.dptr = NULL; + if (cli != NULL){ + key_datum = gdbm_firstkey(inst->gdbm); + while (key_datum.dptr) { + data_datum = gdbm_fetch(inst->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 with active=1 + * then we use that for multilink (MPPP) to work properly. + */ + if (strcmp(entry.cli,cli) == 0 && entry.active){ + mppp = 1; + break; + } + } + + nextkey = gdbm_nextkey(inst->gdbm, key_datum); + free(key_datum.dptr); + key_datum = nextkey; + } + } + + if (!key_datum.dptr){ + key_datum = gdbm_firstkey(inst->gdbm); + while(key_datum.dptr){ + data_datum = gdbm_fetch(inst->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)) || + (inst->max_timeout && request->timestamp >= (entry.timestamp + inst->max_timeout))))){ + datum tmp; + + tmp.dptr = (char *) &entry.ipaddr; + tmp.dsize = sizeof(uint32_t); + data_datum = gdbm_fetch(inst->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 + * 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(inst->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; + + memcpy(key_tmp.key,key_str,16); + key_datum_tmp.dptr = (char *) &key_tmp; + key_datum_tmp.dsize = sizeof(ippool_key); + + data_datum_tmp = gdbm_fetch(inst->gdbm, key_datum_tmp); + if (data_datum_tmp.dptr != NULL){ + + rcode = gdbm_store(inst->gdbm, key_datum, data_datum_tmp, GDBM_REPLACE); + free(data_datum_tmp.dptr); + if (rcode < 0) { + REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno)); + pthread_mutex_unlock(&inst->op_mutex); + return RLM_MODULE_FAIL; + } + } + } 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(inst->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) { + REDEBUG("mppp is not one. Please report this behaviour"); + } + } + } + free(key_datum.dptr); + entry.active = 1; + entry.timestamp = request->timestamp; + if ((vp = fr_pair_find_by_num(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY)) != NULL) { + entry.timeout = (time_t) vp->vp_integer; +#ifdef WITH_DHCP + if (dhcp) { + vp = radius_pair_create(request->reply, &request->reply->vps, + PW_DHCP_IP_ADDRESS_LEASE_TIME, DHCP_MAGIC_VENDOR); + vp->vp_integer = entry.timeout; + fr_pair_delete_by_num(&request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY); + } +#endif + } else { + entry.timeout = 0; + } + if (extra) { + entry.extra = 1; + } + + data_datum.dptr = (char *) &entry; + data_datum.dsize = sizeof(ippool_info); + memcpy(key.key, key_str, 16); + key_datum.dptr = (char *) &key; + key_datum.dsize = sizeof(ippool_key); + + RDEBUG2("Allocating ip to key: '%s'",hex_str); + rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno)); + pthread_mutex_unlock(&inst->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(inst->ip, key_datum); + if (data_datum.dptr){ + memcpy(&num,data_datum.dptr,sizeof(int)); + free(data_datum.dptr); + } else { + num = 0; + } + + num++; + RDEBUG("num: %d",num); + data_datum.dptr = (char *) # + data_datum.dsize = sizeof(int); + rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE); + if (rcode < 0) { + REDEBUG("Failed storing data to %s: %s", inst->ip_index, gdbm_strerror(gdbm_errno)); + pthread_mutex_unlock(&inst->op_mutex); + return RLM_MODULE_FAIL; + } + pthread_mutex_unlock(&inst->op_mutex); + + RDEBUG("Allocated ip %s to client key: %s",ip_ntoa(str,entry.ipaddr),hex_str); + vp = radius_pair_create(request->reply, &request->reply->vps, + attr_ipaddr, vendor_ipaddr); + vp->vp_ipaddr = entry.ipaddr; + + /* + * If there is no Framed-Netmask attribute in the + * reply, add one + */ + if (fr_pair_find_by_num(request->reply->vps, attr_ipmask, vendor_ipaddr, TAG_ANY) == NULL) { + vp = radius_pair_create(request->reply, &request->reply->vps, + attr_ipmask, vendor_ipaddr); + vp->vp_ipaddr = ntohl(inst->netmask); + } + + } + else{ + pthread_mutex_unlock(&inst->op_mutex); + RDEBUG("No available ip addresses in pool"); + return RLM_MODULE_NOTFOUND; + } + + return RLM_MODULE_OK; +} + +static int mod_detach(void *instance) +{ + rlm_ippool_t *inst = instance; + + gdbm_close(inst->gdbm); + gdbm_close(inst->ip); + pthread_mutex_destroy(&inst->op_mutex); + 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. + */ +extern module_t rlm_ippool; +module_t rlm_ippool = { + .magic = RLM_MODULE_INIT, + .name = "ippool", + .type = RLM_TYPE_THREAD_SAFE, + .inst_size = sizeof(rlm_ippool_t), + .config = module_config, + .instantiate = mod_instantiate, + .detach = mod_detach, + .methods = { + + [MOD_ACCOUNTING] = mod_accounting, + [MOD_POST_AUTH] = mod_post_auth + }, +}; |