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