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