Do a memset(0) on the key.nas before doing searches. Nusty bug
[freeradius.git] / src / modules / rlm_ippool / rlm_ippool.c
1 /*
2  * rlm_ippool.c
3  *
4  * Version:  $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  * Copyright 2002  Kostas Kalevras <kkalev@noc.ntua.gr>
22  * 
23  * March 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
24  * - Initial release
25  * April 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
26  * - Add support for the Pool-Name attribute
27  * May 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
28  * - Check the return value of a gdbm_fetch() we didn't check
29  * - Change the nas entry in the ippool_key structure from uint32 to string[64]
30  *   That should allow us to also use the NAS-Identifier attribute
31  * Sep 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
32  * - Move from authorize to post-auth
33  * - Use mutex locks when accessing the gdbm files
34  * - Fail if we don't find nas port information
35  * Oct 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
36  * - Do a memset(0) on the key.nas before doing searches. Nusty bug
37  */
38
39 #include "config.h"
40 #include "autoconf.h"
41 #include "libradius.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <ctype.h>
47
48 #include "radiusd.h"
49 #include "modules.h"
50 #include "conffile.h"
51
52 #include <gdbm.h>
53 #include <time.h>
54 #include <netinet/in.h>
55
56 #ifdef NEEDS_GDBM_SYNC
57 #       define GDBM_SYNCOPT GDBM_SYNC
58 #else
59 #       define GDBM_SYNCOPT 0
60 #endif
61
62 #ifdef GDBM_NOLOCK
63 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
64 #else
65 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
66 #endif
67
68 #define ALL_ONES 4294967295
69 #define MAX_NAS_NAME_SIZE 64
70
71 static const char rcsid[] = "$Id$";
72
73 /*
74  *      Define a structure for our module configuration.
75  *
76  *      These variables do not need to be in a structure, but it's
77  *      a lot cleaner to do so, and a pointer to the structure can
78  *      be used as the instance handle.
79  */
80 typedef struct rlm_ippool_t {
81         char *session_db;
82         char *ip_index;
83         char *name;
84         uint32_t range_start;
85         uint32_t range_stop;
86         uint32_t netmask;
87         int cache_size;
88         GDBM_FILE gdbm;
89         GDBM_FILE ip;
90         pthread_mutex_t session_mutex;
91         pthread_mutex_t ip_mutex;
92 } rlm_ippool_t;
93
94 typedef struct ippool_info {
95         uint32_t        ipaddr;
96         char            active;
97         char            cli[32];
98 } ippool_info;
99
100 typedef struct ippool_key {
101         char nas[MAX_NAS_NAME_SIZE];
102         int port;
103 } ippool_key;
104
105 /*
106  *      A mapping of configuration file names to internal variables.
107  *
108  *      Note that the string is dynamically allocated, so it MUST
109  *      be freed.  When the configuration file parse re-reads the string,
110  *      it free's the old one, and strdup's the new one, placing the pointer
111  *      to the strdup'd string into 'config.string'.  This gets around
112  *      buffer over-flows.
113  */
114 static CONF_PARSER module_config[] = {
115   { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
116   { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
117   { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
118   { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
119   { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
120   { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
121   { NULL, -1, 0, NULL, NULL }
122 };
123
124
125 /*
126  *      Do any per-module initialization that is separate to each
127  *      configured instance of the module.  e.g. set up connections
128  *      to external databases, read configuration files, set up
129  *      dictionary entries, etc.
130  *
131  *      If configuration information is given in the config section
132  *      that must be referenced in later calls, store a handle to it
133  *      in *instance otherwise put a null pointer there.
134  */
135 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
136 {
137         rlm_ippool_t *data;
138         int cache_size;
139         ippool_info entry;
140         ippool_key key;
141         datum key_datum;
142         datum data_datum;
143         int i,j;
144         const char *cli = "0";
145         char *pool_name = NULL;
146         
147         /*
148          *      Set up a storage area for instance data
149          */
150         data = rad_malloc(sizeof(*data));
151
152         /*
153          *      If the configuration parameters can't be parsed, then
154          *      fail.
155          */
156         if (cf_section_parse(conf, data, module_config) < 0) {
157                 free(data);
158                 return -1;
159         }
160         cache_size = data->cache_size;
161
162         if (data->session_db == NULL) {
163                 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
164                 free(data);
165                 return -1;
166         }
167         if (data->ip_index == NULL) {
168                 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
169                 free(data);
170                 return -1;
171         }
172         data->range_start = htonl(data->range_start);
173         data->range_stop = htonl(data->range_stop);
174         data->netmask = htonl(data->netmask);
175         if (data->range_start == 0 || data->range_stop == 0 || \
176                          data->range_start >= data->range_stop ) {
177                 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
178                 free(data);
179                 return -1;
180         }
181         
182         data->gdbm = gdbm_open(data->session_db, sizeof(int),
183                         GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
184         if (data->gdbm == NULL) {
185                 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
186                                 data->session_db, strerror(errno));
187                 return -1;
188         }
189         data->ip = gdbm_open(data->ip_index, sizeof(int),
190                         GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
191         if (data->ip == NULL) {
192                 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
193                                 data->ip_index, strerror(errno));
194                 return -1;
195         }
196         if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
197                 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
198         if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
199                 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
200
201         key_datum = gdbm_firstkey(data->gdbm);
202         if (key_datum.dptr == NULL){
203                         /*
204                          * If the database does not exist initialize it.
205                          * We set the nas/port pairs to not existent values and
206                          * active = 0
207                          */
208                 int rcode;
209                 uint32_t or_result;
210                 char str[32];
211                 const char *nas_init = "NOT_EXIST";
212
213                 DEBUG("rlm_ippool: Initializing database");
214                 for(i=data->range_start,j=-1;i<=data->range_stop;i++,j--){
215
216                         /*
217                          * Net and Broadcast addresses are excluded
218                          */
219                         or_result = i | data->netmask;
220                         if (or_result == data->netmask || or_result == ALL_ONES){
221                                 DEBUG("rlm_ippool: IP %s exlcluded",ip_ntoa(str,ntohl(i)));
222                                 continue;
223                         }
224                         
225                         strcpy(key.nas, nas_init);
226                         key.port = j;
227                         key_datum.dptr = (ippool_key *) &key;
228                         key_datum.dsize = sizeof(ippool_key);
229
230                         entry.ipaddr = ntohl(i);
231                         entry.active = 0;
232                         strcpy(entry.cli,cli);
233
234                         data_datum.dptr = (ippool_info *) &entry;
235                         data_datum.dsize = sizeof(ippool_info);
236
237                         rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
238                         if (rcode < 0) {
239                                 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
240                                                 data->session_db, gdbm_strerror(gdbm_errno));
241                                 free(data);
242                                 gdbm_close(data->gdbm);
243                                 gdbm_close(data->ip);
244                                 return -1;
245                         }
246                 }
247         }
248         else
249                 free(key_datum.dptr);
250
251         /* Add the ip pool name */
252         data->name = NULL;
253         pool_name = cf_section_name2(conf);
254         if (pool_name != NULL)
255                 data->name = strdup(pool_name);
256         pthread_mutex_init(&data->session_mutex, NULL);
257         pthread_mutex_init(&data->ip_mutex, NULL);
258
259         *instance = data;
260         
261         return 0;
262 }
263
264
265 /*
266  *      Check for an Accounting-Stop
267  *      If we find one and we have allocated an IP to this nas/port combination, deallocate it. 
268  */
269 static int ippool_accounting(void *instance, REQUEST *request)
270 {
271         rlm_ippool_t *data = (rlm_ippool_t *)instance;
272         datum key_datum;
273         datum data_datum;
274         int acctstatustype = 0;
275         int port = -1;
276         int rcode;
277         char nas[MAX_NAS_NAME_SIZE];
278         ippool_info entry;
279         ippool_key key;
280         int num = 0;
281         VALUE_PAIR *vp;
282         char str[32];
283
284
285         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
286                 acctstatustype = vp->lvalue;
287         else {
288                 DEBUG("rlm_ippool: Could not find account status type in packet.");
289                 return RLM_MODULE_NOOP;
290         }
291         switch(acctstatustype){
292                 case PW_STATUS_STOP:
293                         if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
294                                 port = vp->lvalue;
295                         else {
296                                 DEBUG("rlm_ippool: Could not find port number in packet.");
297                                 return RLM_MODULE_NOOP;
298                         }
299                         if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
300                                 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
301                         else {
302                                 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
303                                         strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
304                                 else {
305                                         DEBUG("rlm_ippool: Could not find nas information in packet.");
306                                         return RLM_MODULE_NOOP;
307                                 }
308                         }
309                         break;
310                 default:
311                         /* We don't care about any other accounting packet */
312
313                         return RLM_MODULE_NOOP;
314         }
315
316         memset(key.nas,0,MAX_NAS_NAME_SIZE);
317         strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
318         key.port = port;
319         DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",key.nas,key.port);
320         key_datum.dptr = (ippool_key *) &key;
321         key_datum.dsize = sizeof(ippool_key);
322
323         pthread_mutex_lock(&data->session_mutex);
324         data_datum = gdbm_fetch(data->gdbm, key_datum);
325         pthread_mutex_unlock(&data->session_mutex);
326         if (data_datum.dptr != NULL){
327
328                 /*
329                  * If the entry was found set active to zero
330                  */
331                 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
332                 free(data_datum.dptr);
333                 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
334                 entry.active = 0;
335
336                 data_datum.dptr = (ippool_info *) &entry;
337                 data_datum.dsize = sizeof(ippool_info);
338
339                 pthread_mutex_lock(&data->session_mutex);
340                 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
341                 pthread_mutex_unlock(&data->session_mutex);
342                 if (rcode < 0) {
343                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
344                                         data->session_db, gdbm_strerror(gdbm_errno));
345                         return RLM_MODULE_FAIL;
346                 }
347
348                 /*
349                  * Decrease allocated count from the ip index
350                  */
351                 key_datum.dptr = (uint32_t *) &entry.ipaddr;
352                 key_datum.dsize = sizeof(uint32_t);
353                 pthread_mutex_lock(&data->ip_mutex);
354                 data_datum = gdbm_fetch(data->ip, key_datum);
355                 pthread_mutex_unlock(&data->ip_mutex);
356                 if (data_datum.dptr != NULL){
357                         memcpy(&num, data_datum.dptr, sizeof(int));
358                         free(data_datum.dptr);
359                         if (num >0){
360                                 num--;
361                                 DEBUG("rlm_ippool: num: %d",num);
362                                 data_datum.dptr = (int *) &num;
363                                 data_datum.dsize = sizeof(int);
364                                 pthread_mutex_lock(&data->ip_mutex);
365                                 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
366                                 pthread_mutex_unlock(&data->ip_mutex);
367                                 if (rcode < 0) {
368                                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
369                                                         data->ip_index, gdbm_strerror(gdbm_errno));
370                                         return RLM_MODULE_FAIL;
371                                 }
372                         }
373                 }
374         }
375         else
376                 DEBUG("rlm_ippool: Entry not found");
377
378         return RLM_MODULE_OK;
379 }
380
381 static int ippool_postauth(void *instance, REQUEST *request)
382 {
383         rlm_ippool_t *data = (rlm_ippool_t *) instance;
384         int port = 0;
385         int delete = 0;
386         int rcode;
387         int num = 0;
388         char nas[MAX_NAS_NAME_SIZE];
389         datum key_datum;
390         datum nextkey;
391         datum data_datum;
392         ippool_key key;
393         ippool_info entry;
394         VALUE_PAIR *vp;
395         char *cli = NULL;
396         char str[32];
397
398
399         /* quiet the compiler */
400         instance = instance;
401         request = request;
402
403         /* Check if Pool-Name attribute exists. If it exists check our name and
404          * run only if they match
405          */
406         if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
407                 if (data->name == NULL || strcmp(data->name,vp->strvalue))
408                         return RLM_MODULE_NOOP;
409         } else {
410                 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
411                 return RLM_MODULE_NOOP;
412         }
413
414         /*
415          * Get the nas ip address
416          * If not fail
417          */
418         if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
419                 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
420         else{
421                 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
422                         strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
423                 else{
424                         DEBUG("rlm_ippool: Could not find nas information.");
425                         return RLM_MODULE_NOOP;
426                 }
427         }
428
429         /*
430          * Find the caller id
431          */
432         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
433                 cli = vp->strvalue;
434
435         /*
436          * Find the port
437          * If not fail
438          */
439         if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
440                 port = vp->lvalue;
441         else{
442                 DEBUG("rlm_ippool: Could not find port information.");
443                 return RLM_MODULE_NOOP;
444         }
445
446         memset(key.nas,0,MAX_NAS_NAME_SIZE);
447         strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
448         key.port = port;        
449         DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",key.nas,key.port);
450         key_datum.dptr = (ippool_key *) &key;
451         key_datum.dsize = sizeof(ippool_key);
452
453         pthread_mutex_lock(&data->session_mutex);
454         data_datum = gdbm_fetch(data->gdbm, key_datum);
455         pthread_mutex_unlock(&data->session_mutex);
456         if (data_datum.dptr != NULL){
457                 /*
458                  * If there is a corresponding entry in the database with active=1 it is stale.
459                  * Set active to zero
460                  */
461                 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
462                 free(data_datum.dptr);
463                 if (entry.active){
464                         DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
465                         entry.active = 0;
466
467                         data_datum.dptr = (ippool_info *) &entry;
468                         data_datum.dsize = sizeof(ippool_info);
469
470                         pthread_mutex_lock(&data->session_mutex);
471                         rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
472                         pthread_mutex_unlock(&data->session_mutex);
473                         if (rcode < 0) {
474                                 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
475                                         data->session_db, gdbm_strerror(gdbm_errno));
476                                 return RLM_MODULE_FAIL;
477                         }
478                         /* Decrease allocated count from the ip index */
479
480                         key_datum.dptr = (uint32_t *) &entry.ipaddr;
481                         key_datum.dsize = sizeof(uint32_t);
482                         pthread_mutex_lock(&data->ip_mutex);
483                         data_datum = gdbm_fetch(data->ip, key_datum);
484                         pthread_mutex_unlock(&data->ip_mutex);
485                         if (data_datum.dptr != NULL){
486                                 memcpy(&num, data_datum.dptr, sizeof(int));
487                                 free(data_datum.dptr);
488                                 if (num >0){
489                                         num--;
490                                         DEBUG("rlm_ippool: num: %d",num);
491                                         data_datum.dptr = (int *) &num;
492                                         data_datum.dsize = sizeof(int);
493                                         pthread_mutex_lock(&data->ip_mutex);
494                                         rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
495                                         pthread_mutex_unlock(&data->ip_mutex);
496                                         if (rcode < 0) {
497                                                 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
498                                                                 data->ip_index, gdbm_strerror(gdbm_errno));
499                                                 return RLM_MODULE_FAIL;
500                                         }
501                                 }
502                         }
503                 }
504         }
505         /*
506          * If there is a Framed-IP-Address attribute in the reply do nothing
507          */
508         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
509                 return RLM_MODULE_NOOP;
510
511         /*
512          * Walk through the database searching for an active=0 entry.
513          */
514
515         pthread_mutex_lock(&data->session_mutex);
516         key_datum = gdbm_firstkey(data->gdbm);
517         while(key_datum.dptr){
518                 data_datum = gdbm_fetch(data->gdbm, key_datum);
519                 if (data_datum.dptr){
520                         memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
521                         free(data_datum.dptr);  
522                         /*
523                         * If we find an entry for the same caller-id and nas with active=1
524                         * then we use that for multilink (MPPP) to work properly.
525                         */
526                         if (cli != NULL && strcmp(entry.cli,cli) == 0 && entry.active){
527                                 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
528                                 if (!strcmp(key.nas,nas))
529                                         break;
530                         }
531                         if (entry.active == 0){
532                                 datum tmp;              
533
534                                 tmp.dptr = (uint32_t *) &entry.ipaddr;
535                                 tmp.dsize = sizeof(uint32_t);
536                                 pthread_mutex_lock(&data->ip_mutex);
537                                 data_datum = gdbm_fetch(data->ip, tmp);
538                                 pthread_mutex_unlock(&data->ip_mutex);
539
540                                 /*
541                                  * If we find an entry in the ip index and the number is zero (meaning
542                                  * that we haven't allocated the same ip address to another nas/port pair)
543                                  * or if we don't find an entry then delete the session entry so
544                                  * that we can change the key (nas/port)
545                                  * Else we don't delete the session entry since we haven't yet deallocated the
546                                  * corresponding ip address and we continue our search.
547                                  */
548
549                                 if (data_datum.dptr){
550                                         memcpy(&num,data_datum.dptr, sizeof(int));
551                                         free(data_datum.dptr);
552                                         if (num == 0){
553                                                 delete = 1;
554                                                 break;
555                                         }
556                                 }
557                                 else{
558                                         delete = 1;
559                                         break;
560                                 }
561                         }
562                 }
563                 nextkey = gdbm_nextkey(data->gdbm, key_datum);
564                 free(key_datum.dptr);
565                 key_datum = nextkey;
566         }
567         pthread_mutex_unlock(&data->session_mutex);
568         /*
569          * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
570          * the reply
571          */
572         if (key_datum.dptr){
573                 entry.active = 1;
574                 data_datum.dptr = (ippool_info *) &entry;
575                 data_datum.dsize = sizeof(ippool_info);
576
577                 if (delete){
578                         /*
579                          * Delete the entry so that we can change the key
580                          */
581                         pthread_mutex_lock(&data->session_mutex);
582                         gdbm_delete(data->gdbm, key_datum);
583                         pthread_mutex_unlock(&data->session_mutex);
584                 }
585                 free(key_datum.dptr);
586                 memset(key.nas,0,MAX_NAS_NAME_SIZE);
587                 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
588                 key.port = port;
589                 key_datum.dptr = (ippool_key *) &key;
590                 key_datum.dsize = sizeof(ippool_key);
591                 
592                 DEBUG2("rlm_ippool: Allocating ip to nas/port: %s/%d",key.nas,key.port);
593                 pthread_mutex_lock(&data->session_mutex);
594                 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
595                 pthread_mutex_unlock(&data->session_mutex);
596                 if (rcode < 0) {
597                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
598                                 data->session_db, gdbm_strerror(gdbm_errno));
599                         return RLM_MODULE_FAIL;
600                 }
601
602                 /* Increase the ip index count */
603                 key_datum.dptr = (uint32_t *) &entry.ipaddr;
604                 key_datum.dsize = sizeof(uint32_t);     
605                 pthread_mutex_lock(&data->ip_mutex);
606                 data_datum = gdbm_fetch(data->ip, key_datum);
607                 pthread_mutex_unlock(&data->ip_mutex);
608                 if (data_datum.dptr){
609                         memcpy(&num,data_datum.dptr,sizeof(int));
610                         free(data_datum.dptr);
611                 }
612                 num++;
613                 DEBUG("rlm_ippool: num: %d",num);
614                 data_datum.dptr = (int *) &num;
615                 data_datum.dsize = sizeof(int);
616                 pthread_mutex_lock(&data->ip_mutex);
617                 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
618                 pthread_mutex_unlock(&data->ip_mutex);
619                 if (rcode < 0) {
620                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
621                                 data->ip_index, gdbm_strerror(gdbm_errno));
622                         return RLM_MODULE_FAIL;
623                 }
624                         
625
626                 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %d",ip_ntoa(str,entry.ipaddr),
627                                 key.nas,port);
628                 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
629                         radlog(L_ERR|L_CONS, "no memory");
630                         return RLM_MODULE_NOOP;
631                 }
632                 vp->lvalue = entry.ipaddr;
633                 pairadd(&request->reply->vps, vp);
634         }
635         else{
636                 DEBUG("rlm_ippool: No available ip addresses in pool.");
637                 return RLM_MODULE_NOOP;
638         }
639
640         return RLM_MODULE_OK;
641 }
642
643 static int ippool_detach(void *instance)
644 {
645         rlm_ippool_t *data = (rlm_ippool_t *) instance;
646
647         gdbm_close(data->gdbm);
648         gdbm_close(data->ip);
649         free(data->session_db);
650         free(data->ip_index);
651         pthread_mutex_destroy(&data->session_mutex);
652         pthread_mutex_destroy(&data->ip_mutex);
653
654         free(instance);
655         return 0;
656 }
657
658 /*
659  *      The module name should be the only globally exported symbol.
660  *      That is, everything else should be 'static'.
661  *
662  *      If the module needs to temporarily modify it's instantiation
663  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
664  *      The server will then take care of ensuring that the module
665  *      is single-threaded.
666  */
667 module_t rlm_ippool = {
668         "IPPOOL",       
669         RLM_TYPE_THREAD_SAFE,           /* type */
670         NULL,                           /* initialization */
671         ippool_instantiate,             /* instantiation */
672         {
673                 NULL,                   /* authentication */
674                 NULL,                   /* authorization */
675                 NULL,                   /* preaccounting */
676                 ippool_accounting,      /* accounting */
677                 NULL,                   /* checksimul */
678                 NULL,                   /* pre-proxy */
679                 NULL,                   /* post-proxy */
680                 ippool_postauth         /* post-auth */
681         },
682         ippool_detach,                  /* detach */
683         NULL,                           /* destroy */
684 };