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