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.
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.
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
20 * Copyright 2001 The FreeRADIUS server project
21 * Copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr>
23 * March 2002, Kostas Kalevras <kkalev@noc.ntua.gr>
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.
51 #include "libradius.h"
64 #include <netinet/in.h>
66 #ifdef NEEDS_GDBM_SYNC
67 # define GDBM_SYNCOPT GDBM_SYNC
69 # define GDBM_SYNCOPT 0
73 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
75 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
78 #define MAX_NAS_NAME_SIZE 64
80 static const char rcsid[] = "$Id$";
83 * Define a structure for our module configuration.
85 * These variables do not need to be in a structure, but it's
86 * a lot cleaner to do so, and a pointer to the structure can
87 * be used as the instance handle.
89 typedef struct rlm_ippool_t {
100 pthread_mutex_t op_mutex;
103 typedef struct ippool_info {
110 typedef struct ippool_key {
111 char nas[MAX_NAS_NAME_SIZE];
116 * A mapping of configuration file names to internal variables.
118 * Note that the string is dynamically allocated, so it MUST
119 * be freed. When the configuration file parse re-reads the string,
120 * it free's the old one, and strdup's the new one, placing the pointer
121 * to the strdup'd string into 'config.string'. This gets around
124 static CONF_PARSER module_config[] = {
125 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
126 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
127 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
128 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
129 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
130 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
131 { "override", PW_TYPE_BOOLEAN, offsetof(rlm_ippool_t,override), NULL, "no" },
132 { NULL, -1, 0, NULL, NULL }
137 * Do any per-module initialization that is separate to each
138 * configured instance of the module. e.g. set up connections
139 * to external databases, read configuration files, set up
140 * dictionary entries, etc.
142 * If configuration information is given in the config section
143 * that must be referenced in later calls, store a handle to it
144 * in *instance otherwise put a null pointer there.
146 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
156 const char *cli = "0";
157 char *pool_name = NULL;
160 * Set up a storage area for instance data
162 data = rad_malloc(sizeof(*data));
166 memset(data, 0, sizeof(*data));
169 * If the configuration parameters can't be parsed, then
172 if (cf_section_parse(conf, data, module_config) < 0) {
176 cache_size = data->cache_size;
178 if (data->session_db == NULL) {
179 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
183 if (data->ip_index == NULL) {
184 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
188 data->range_start = htonl(data->range_start);
189 data->range_stop = htonl(data->range_stop);
190 data->netmask = htonl(data->netmask);
191 if (data->range_start == 0 || data->range_stop == 0 || \
192 data->range_start >= data->range_stop ) {
193 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
198 data->gdbm = gdbm_open(data->session_db, sizeof(int),
199 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
200 if (data->gdbm == NULL) {
201 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
202 data->session_db, strerror(errno));
205 data->ip = gdbm_open(data->ip_index, sizeof(int),
206 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
207 if (data->ip == NULL) {
208 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
209 data->ip_index, strerror(errno));
212 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
213 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
214 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
215 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
217 key_datum = gdbm_firstkey(data->gdbm);
218 if (key_datum.dptr == NULL){
220 * If the database does not exist initialize it.
221 * We set the nas/port pairs to not existent values and
227 const char *nas_init = "NOT_EXIST";
229 DEBUG("rlm_ippool: Initializing database");
230 for(i=data->range_start,j=~0;i<=data->range_stop;i++,j--){
233 * Net and Broadcast addresses are excluded
235 or_result = i | data->netmask;
236 if (~data->netmask != 0 &&
237 (or_result == data->netmask ||
238 (~or_result == 0))) {
239 DEBUG("rlm_ippool: IP %s excluded",
240 ip_ntoa(str, ntohl(i)));
244 strcpy(key.nas, nas_init);
246 key_datum.dptr = (char *) &key;
247 key_datum.dsize = sizeof(ippool_key);
249 entry.ipaddr = ntohl(i);
252 strcpy(entry.cli,cli);
254 data_datum.dptr = (char *) &entry;
255 data_datum.dsize = sizeof(ippool_info);
257 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
259 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
260 data->session_db, gdbm_strerror(gdbm_errno));
262 gdbm_close(data->gdbm);
263 gdbm_close(data->ip);
269 free(key_datum.dptr);
271 /* Add the ip pool name */
273 pool_name = cf_section_name2(conf);
274 if (pool_name != NULL)
275 data->name = strdup(pool_name);
276 pthread_mutex_init(&data->op_mutex, NULL);
285 * Check for an Accounting-Stop
286 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
288 static int ippool_accounting(void *instance, REQUEST *request)
290 rlm_ippool_t *data = (rlm_ippool_t *)instance;
294 int acctstatustype = 0;
295 unsigned int port = ~0;
297 char nas[MAX_NAS_NAME_SIZE];
305 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
306 acctstatustype = vp->lvalue;
308 DEBUG("rlm_ippool: Could not find account status type in packet. Return NOOP.");
309 return RLM_MODULE_NOOP;
311 switch(acctstatustype){
313 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
316 DEBUG("rlm_ippool: Could not find port number in packet. Return NOOP.");
317 return RLM_MODULE_NOOP;
319 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
320 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
322 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
323 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
325 DEBUG("rlm_ippool: Could not find nas information in packet. Return NOOP.");
326 return RLM_MODULE_NOOP;
331 /* We don't care about any other accounting packet */
332 DEBUG("rlm_ippool: This is not an Accounting-Stop. Return NOOP.");
334 return RLM_MODULE_NOOP;
337 memset(key.nas,0,MAX_NAS_NAME_SIZE);
338 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
340 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key.nas,key.port);
341 key_datum.dptr = (char *) &key;
342 key_datum.dsize = sizeof(ippool_key);
344 pthread_mutex_lock(&data->op_mutex);
345 data_datum = gdbm_fetch(data->gdbm, key_datum);
346 if (data_datum.dptr != NULL){
349 * If the entry was found set active to zero
351 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
352 free(data_datum.dptr);
353 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%u",ip_ntoa(str,entry.ipaddr),port);
357 * Save the reference to the entry
359 save_datum.dptr = key_datum.dptr;
360 save_datum.dsize = key_datum.dsize;
362 data_datum.dptr = (char *) &entry;
363 data_datum.dsize = sizeof(ippool_info);
365 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
367 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
368 data->session_db, gdbm_strerror(gdbm_errno));
369 pthread_mutex_unlock(&data->op_mutex);
370 return RLM_MODULE_FAIL;
374 * Decrease allocated count from the ip index
376 key_datum.dptr = (char *) &entry.ipaddr;
377 key_datum.dsize = sizeof(uint32_t);
378 data_datum = gdbm_fetch(data->ip, key_datum);
379 if (data_datum.dptr != NULL){
380 memcpy(&num, data_datum.dptr, sizeof(int));
381 free(data_datum.dptr);
384 DEBUG("rlm_ippool: num: %d",num);
385 data_datum.dptr = (char *) #
386 data_datum.dsize = sizeof(int);
387 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
389 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
390 data->ip_index, gdbm_strerror(gdbm_errno));
391 pthread_mutex_unlock(&data->op_mutex);
392 return RLM_MODULE_FAIL;
394 if (num >0 && entry.extra == 1){
396 * We are doing MPPP and we still have nas/port entries referencing
397 * this ip. Delete this entry so that eventually we only keep one
398 * reference to this ip.
400 gdbm_delete(data->gdbm,save_datum);
404 pthread_mutex_unlock(&data->op_mutex);
407 pthread_mutex_unlock(&data->op_mutex);
408 DEBUG("rlm_ippool: Entry not found");
411 return RLM_MODULE_OK;
414 static int ippool_postauth(void *instance, REQUEST *request)
416 rlm_ippool_t *data = (rlm_ippool_t *) instance;
417 unsigned int port = 0;
424 char nas[MAX_NAS_NAME_SIZE];
436 /* quiet the compiler */
440 /* Check if Pool-Name attribute exists. If it exists check our name and
441 * run only if they match
443 if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
444 if (data->name == NULL || strcmp(data->name,vp->strvalue))
445 return RLM_MODULE_NOOP;
447 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
448 return RLM_MODULE_NOOP;
452 * Get the nas ip address
455 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
456 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
458 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
459 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
461 DEBUG("rlm_ippool: Could not find nas information. Return NOOP.");
462 return RLM_MODULE_NOOP;
469 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
476 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
479 DEBUG("rlm_ippool: Could not find nas port information. Return NOOP.");
480 return RLM_MODULE_NOOP;
483 memset(key.nas,0,MAX_NAS_NAME_SIZE);
484 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
486 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key.nas,key.port);
487 key_datum.dptr = (char *) &key;
488 key_datum.dsize = sizeof(ippool_key);
490 pthread_mutex_lock(&data->op_mutex);
491 data_datum = gdbm_fetch(data->gdbm, key_datum);
492 if (data_datum.dptr != NULL){
494 * If there is a corresponding entry in the database with active=1 it is stale.
498 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
499 free(data_datum.dptr);
501 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%u",ip_ntoa(str,entry.ipaddr),port);
505 * Save the reference to the entry
507 save_datum.dptr = key_datum.dptr;
508 save_datum.dsize = key_datum.dsize;
510 data_datum.dptr = (char *) &entry;
511 data_datum.dsize = sizeof(ippool_info);
513 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
515 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
516 data->session_db, gdbm_strerror(gdbm_errno));
517 pthread_mutex_unlock(&data->op_mutex);
518 return RLM_MODULE_FAIL;
520 /* Decrease allocated count from the ip index */
522 key_datum.dptr = (char *) &entry.ipaddr;
523 key_datum.dsize = sizeof(uint32_t);
524 data_datum = gdbm_fetch(data->ip, key_datum);
525 if (data_datum.dptr != NULL){
526 memcpy(&num, data_datum.dptr, sizeof(int));
527 free(data_datum.dptr);
530 DEBUG("rlm_ippool: num: %d",num);
531 data_datum.dptr = (char *) #
532 data_datum.dsize = sizeof(int);
533 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
535 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
536 data->ip_index, gdbm_strerror(gdbm_errno));
537 pthread_mutex_unlock(&data->op_mutex);
538 return RLM_MODULE_FAIL;
540 if (num >0 && entry.extra == 1){
542 * We are doing MPPP and we still have nas/port entries referencing
543 * this ip. Delete this entry so that eventually we only keep one
544 * reference to this ip.
546 gdbm_delete(data->gdbm,save_datum);
553 pthread_mutex_unlock(&data->op_mutex);
556 * If there is a Framed-IP-Address attribute in the reply, check for override
558 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
561 /* Override supplied Framed-IP-Address */
562 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
565 return RLM_MODULE_NOOP;
570 * Walk through the database searching for an active=0 entry.
571 * We search twice. Once to see if we have an active entry with the same callerid
572 * so that MPPP can work ok and then once again to find a free entry.
575 pthread_mutex_lock(&data->op_mutex);
577 key_datum.dptr = NULL;
579 key_datum = gdbm_firstkey(data->gdbm);
580 while(key_datum.dptr){
581 data_datum = gdbm_fetch(data->gdbm, key_datum);
582 if (data_datum.dptr){
583 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
584 free(data_datum.dptr);
586 * If we find an entry for the same caller-id and nas with active=1
587 * then we use that for multilink (MPPP) to work properly.
589 if (strcmp(entry.cli,cli) == 0 && entry.active){
590 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
591 if (!strcmp(key.nas,nas)){
597 nextkey = gdbm_nextkey(data->gdbm, key_datum);
598 free(key_datum.dptr);
603 if (key_datum.dptr == NULL){
604 key_datum = gdbm_firstkey(data->gdbm);
605 while(key_datum.dptr){
606 data_datum = gdbm_fetch(data->gdbm, key_datum);
607 if (data_datum.dptr){
608 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
609 free(data_datum.dptr);
612 * Find an entry with active == 0
614 if (entry.active == 0){
617 tmp.dptr = (char *) &entry.ipaddr;
618 tmp.dsize = sizeof(uint32_t);
619 data_datum = gdbm_fetch(data->ip, tmp);
622 * If we find an entry in the ip index and the number is zero (meaning
623 * that we haven't allocated the same ip address to another nas/port pair)
624 * or if we don't find an entry then delete the session entry so
625 * that we can change the key (nas/port)
626 * Else we don't delete the session entry since we haven't yet deallocated the
627 * corresponding ip address and we continue our search.
630 if (data_datum.dptr){
631 memcpy(&num,data_datum.dptr, sizeof(int));
632 free(data_datum.dptr);
644 nextkey = gdbm_nextkey(data->gdbm, key_datum);
645 free(key_datum.dptr);
650 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
652 * We keep the operation mutex locked until after we have set the corresponding entry active
657 * Found == 1 means we have the nas/port combination entry in our database
658 * We exchange the ip address between the nas/port entry and the free entry
659 * Afterwards we will save the free ip address to the nas/port entry.
661 * ---------------------------------------------
662 * - NAS/PORT Entry |||| Free Entry ||| Time
663 * - IP1 IP2(Free) BEFORE
664 * - IP2(Free) IP1 AFTER
665 * ---------------------------------------------
667 * We only do this if we are NOT doing MPPP
671 datum data_datum_tmp;
674 memset(key_tmp.nas,0,MAX_NAS_NAME_SIZE);
675 strncpy(key_tmp.nas,nas,MAX_NAS_NAME_SIZE -1 );
677 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key_tmp.nas,key_tmp.port);
678 key_datum_tmp.dptr = (char *) &key_tmp;
679 key_datum_tmp.dsize = sizeof(ippool_key);
681 data_datum_tmp = gdbm_fetch(data->gdbm, key_datum_tmp);
682 if (data_datum_tmp.dptr != NULL){
684 rcode = gdbm_store(data->gdbm, key_datum, data_datum_tmp, GDBM_REPLACE);
686 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
687 data->session_db, gdbm_strerror(gdbm_errno));
688 pthread_mutex_unlock(&data->op_mutex);
689 return RLM_MODULE_FAIL;
691 free(data_datum_tmp.dptr);
696 * We have not found the nas/port combination
700 * Delete the entry so that we can change the key
701 * All is well. We delete one entry and we add one entry
703 gdbm_delete(data->gdbm, key_datum);
707 * We are doing MPPP. (mppp should be 1)
708 * We don't do anything.
709 * We will create an extra not needed entry in the database in this case
710 * but we don't really care since we always also use the ip_index database
711 * when we search for a free entry.
712 * We will also delete that entry on the accounting section so that we only
713 * have one nas/port entry referencing each ip
718 radlog(L_ERR, "rlm_ippool: mppp is not one. Please report this behaviour.");
721 free(key_datum.dptr);
725 data_datum.dptr = (char *) &entry;
726 data_datum.dsize = sizeof(ippool_info);
727 memset(key.nas,0,MAX_NAS_NAME_SIZE);
728 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
730 key_datum.dptr = (char *) &key;
731 key_datum.dsize = sizeof(ippool_key);
733 DEBUG2("rlm_ippool: Allocating ip to nas/port: %s/%u",key.nas,key.port);
734 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
736 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
737 data->session_db, gdbm_strerror(gdbm_errno));
738 pthread_mutex_unlock(&data->op_mutex);
739 return RLM_MODULE_FAIL;
742 /* Increase the ip index count */
743 key_datum.dptr = (char *) &entry.ipaddr;
744 key_datum.dsize = sizeof(uint32_t);
745 data_datum = gdbm_fetch(data->ip, key_datum);
746 if (data_datum.dptr){
747 memcpy(&num,data_datum.dptr,sizeof(int));
748 free(data_datum.dptr);
752 DEBUG("rlm_ippool: num: %d",num);
753 data_datum.dptr = (char *) #
754 data_datum.dsize = sizeof(int);
755 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
757 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
758 data->ip_index, gdbm_strerror(gdbm_errno));
759 pthread_mutex_unlock(&data->op_mutex);
760 return RLM_MODULE_FAIL;
762 pthread_mutex_unlock(&data->op_mutex);
765 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %u",ip_ntoa(str,entry.ipaddr),
767 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
768 radlog(L_ERR|L_CONS, "no memory");
769 return RLM_MODULE_NOOP;
771 vp->lvalue = entry.ipaddr;
772 ip_ntoa(vp->strvalue, vp->lvalue);
773 pairadd(&request->reply->vps, vp);
776 * If there is no Framed-Netmask attribute in the
779 if (pairfind(request->reply->vps, PW_FRAMED_IP_NETMASK) == NULL) {
780 if ((vp = paircreate(PW_FRAMED_IP_NETMASK, PW_TYPE_IPADDR)) == NULL)
781 radlog(L_ERR|L_CONS, "no memory");
783 vp->lvalue = ntohl(data->netmask);
784 ip_ntoa(vp->strvalue, vp->lvalue);
785 pairadd(&request->reply->vps, vp);
791 pthread_mutex_unlock(&data->op_mutex);
792 DEBUG("rlm_ippool: No available ip addresses in pool.");
793 return RLM_MODULE_NOOP;
796 return RLM_MODULE_OK;
799 static int ippool_detach(void *instance)
801 rlm_ippool_t *data = (rlm_ippool_t *) instance;
803 gdbm_close(data->gdbm);
804 gdbm_close(data->ip);
805 free(data->session_db);
806 free(data->ip_index);
807 pthread_mutex_destroy(&data->op_mutex);
814 * The module name should be the only globally exported symbol.
815 * That is, everything else should be 'static'.
817 * If the module needs to temporarily modify it's instantiation
818 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
819 * The server will then take care of ensuring that the module
820 * is single-threaded.
822 module_t rlm_ippool = {
824 RLM_TYPE_THREAD_SAFE, /* type */
825 NULL, /* initialization */
826 ippool_instantiate, /* instantiation */
828 NULL, /* authentication */
829 NULL, /* authorization */
830 NULL, /* preaccounting */
831 ippool_accounting, /* accounting */
832 NULL, /* checksimul */
833 NULL, /* pre-proxy */
834 NULL, /* post-proxy */
835 ippool_postauth /* post-auth */
837 ippool_detach, /* detach */