Print a message when ippool_authorize can't find the Pool-Name attribute
[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                 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
410                 return RLM_MODULE_NOOP;
411         }
412
413         /*
414          * Get the nas ip address
415          */
416         if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
417                 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
418         else{
419                 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
420                         strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
421                 else{
422                         DEBUG("rlm_ippool: Could not find nas information.");
423                         return RLM_MODULE_NOOP;
424                 }
425         }
426
427         /*
428          * Find the caller id
429          */
430         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
431                 cli = vp->strvalue;
432
433         /*
434          * Find the port
435          */
436         if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL){
437                 port = vp->lvalue;
438
439                 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
440                 key.port = port;        
441                 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",nas,port);
442                 key_datum.dptr = (ippool_key *) &key;
443                 key_datum.dsize = sizeof(ippool_key);
444
445                 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
446                 data_datum = gdbm_fetch(data->gdbm, key_datum);
447                 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
448                 if (data_datum.dptr != NULL){
449                         /*
450                          * If there is a corresponding entry in the database with active=1 it is stale.
451                          * Set active to zero
452                          */
453                         memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
454                         free(data_datum.dptr);
455                         if (entry.active){
456                                 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
457                                 entry.active = 0;
458
459                                 data_datum.dptr = (ippool_info *) &entry;
460                                 data_datum.dsize = sizeof(ippool_info);
461
462                                 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
463                                 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
464                                 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
465                                 if (rcode < 0) {
466                                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
467                                                 data->session_db, gdbm_strerror(gdbm_errno));
468                                         return RLM_MODULE_FAIL;
469                                 }
470                                 /* Decrease allocated count from the ip index */
471
472                                 key_datum.dptr = (uint32_t *) &entry.ipaddr;
473                                 key_datum.dsize = sizeof(uint32_t);
474                                 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
475                                 data_datum = gdbm_fetch(data->ip, key_datum);
476                                 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
477                                 if (data_datum.dptr != NULL){
478                                         memcpy(&num, data_datum.dptr, sizeof(int));
479                                         free(data_datum.dptr);
480                                         if (num >0){
481                                                 num--;
482                                                 DEBUG("rlm_ippool: num: %d",num);
483                                                 data_datum.dptr = (int *) &num;
484                                                 data_datum.dsize = sizeof(int);
485                                                 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
486                                                 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
487                                                 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
488                                                 if (rcode < 0) {
489                                                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
490                                                                         data->ip_index, gdbm_strerror(gdbm_errno));
491                                                         return RLM_MODULE_FAIL;
492                                                 }
493                                         }
494                                 }
495                         }
496                 }
497         }
498         /*
499          * If there is a Framed-IP-Address attribute in the reply do nothing
500          */
501         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
502                 return RLM_MODULE_NOOP;
503
504         /*
505          * Walk through the database searching for an active=0 entry.
506          */
507
508         if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
509         key_datum = gdbm_firstkey(data->gdbm);
510         while(key_datum.dptr){
511                 data_datum = gdbm_fetch(data->gdbm, key_datum);
512                 if (data_datum.dptr){
513                         memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
514                         free(data_datum.dptr);  
515                         /*
516                         * If we find an entry for the same caller-id and nas with active=1
517                         * then we use that for multilink (MPPP) to work properly.
518                         */
519                         if (cli != NULL && strcmp(entry.cli,cli) == 0 && entry.active){
520                                 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
521                                 if (!strcmp(key.nas,nas))
522                                         break;
523                         }
524                         if (entry.active == 0){
525                                 datum tmp;              
526
527                                 tmp.dptr = (uint32_t *) &entry.ipaddr;
528                                 tmp.dsize = sizeof(uint32_t);
529                                 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
530                                 data_datum = gdbm_fetch(data->ip, tmp);
531                                 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
532
533                                 /*
534                                  * If we find an entry in the ip index and the number is zero (meaning
535                                  * that we haven't allocated the same ip address to another nas/port pair)
536                                  * or if we don't find an entry then delete the session entry so
537                                  * that we can change the key (nas/port)
538                                  * Else we don't delete the session entry since we haven't yet deallocated the
539                                  * corresponding ip address and we continue our search.
540                                  */
541
542                                 if (data_datum.dptr){
543                                         memcpy(&num,data_datum.dptr, sizeof(int));
544                                         free(data_datum.dptr);
545                                         if (num == 0){
546                                                 delete = 1;
547                                                 break;
548                                         }
549                                 }
550                                 else{
551                                         delete = 1;
552                                         break;
553                                 }
554                         }
555                 }
556                 nextkey = gdbm_nextkey(data->gdbm, key_datum);
557                 free(key_datum.dptr);
558                 key_datum = nextkey;
559         }
560         if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
561         /*
562          * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
563          * the reply
564          */
565         if (key_datum.dptr){
566                 entry.active = 1;
567                 data_datum.dptr = (ippool_info *) &entry;
568                 data_datum.dsize = sizeof(ippool_info);
569
570                 if (delete){
571                         /*
572                          * Delete the entry so that we can change the key
573                          */
574                         if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
575                         gdbm_delete(data->gdbm, key_datum);
576                         if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
577                 }
578                 free(key_datum.dptr);
579                 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
580                 key.port = port;
581                 key_datum.dptr = (ippool_key *) &key;
582                 key_datum.dsize = sizeof(ippool_key);
583                 
584                 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
585                 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
586                 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
587                 if (rcode < 0) {
588                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
589                                 data->session_db, gdbm_strerror(gdbm_errno));
590                         return RLM_MODULE_FAIL;
591                 }
592
593                 /* Increase the ip index count */
594                 key_datum.dptr = (uint32_t *) &entry.ipaddr;
595                 key_datum.dsize = sizeof(uint32_t);     
596                 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
597                 data_datum = gdbm_fetch(data->ip, key_datum);
598                 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
599                 if (data_datum.dptr){
600                         memcpy(&num,data_datum.dptr,sizeof(int));
601                         free(data_datum.dptr);
602                 }
603                 num++;
604                 DEBUG("rlm_ippool: num: %d",num);
605                 data_datum.dptr = (int *) &num;
606                 data_datum.dsize = sizeof(int);
607                 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
608                 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
609                 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
610                 if (rcode < 0) {
611                         radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
612                                 data->ip_index, gdbm_strerror(gdbm_errno));
613                         return RLM_MODULE_FAIL;
614                 }
615                         
616
617                 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %d",ip_ntoa(str,entry.ipaddr),
618                                 key.nas,port);
619                 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
620                         radlog(L_ERR|L_CONS, "no memory");
621                         return RLM_MODULE_NOOP;
622                 }
623                 vp->lvalue = entry.ipaddr;
624                 pairadd(&request->reply->vps, vp);
625         }
626         else{
627                 DEBUG("rlm_ippool: No available ip addresses in pool.");
628                 return RLM_MODULE_NOOP;
629         }
630
631         return RLM_MODULE_OK;
632 }
633
634 static int ippool_detach(void *instance)
635 {
636         rlm_ippool_t *data = (rlm_ippool_t *) instance;
637
638         gdbm_close(data->gdbm);
639         gdbm_close(data->ip);
640         free(data->session_db);
641         free(data->ip_index);
642
643         free(instance);
644         return 0;
645 }
646
647 /*
648  *      The module name should be the only globally exported symbol.
649  *      That is, everything else should be 'static'.
650  *
651  *      If the module needs to temporarily modify it's instantiation
652  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
653  *      The server will then take care of ensuring that the module
654  *      is single-threaded.
655  */
656 module_t rlm_ippool = {
657         "IPPOOL",       
658         RLM_TYPE_THREAD_SAFE,           /* type */
659         NULL,                           /* initialization */
660         ippool_instantiate,             /* instantiation */
661         {
662                 NULL,                   /* authentication */
663                 ippool_authorize,       /* authorization */
664                 NULL,                   /* preaccounting */
665                 ippool_accounting,      /* accounting */
666                 NULL                    /* checksimul */
667         },
668         ippool_detach,                  /* detach */
669         NULL,                           /* destroy */
670 };