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