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