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.
47 * Mar 2004, Kostas Kalevras <kkalev@noc.ntua.gr>
48 * - Add a timestamp and a timeout attribute in ippool_info. When we assign an ip we set timestamp
49 * to request->timestamp and timeout to %{Session-Timeout:-0}. When we search for a free entry
50 * we check if timeout has expired. If it has then we free the entry. We also add a maximum
51 * timeout configuration directive. If it is non zero then we also use that one to free entries.
52 * Jul 2004, Kostas Kalevras <kkalev@noc.ntua.gr>
53 * - If Pool-Name is set to DEFAULT then always run.
54 * Mar 2005, Kostas Kalevras <kkalev@noc.ntua.gr>
55 * - Make the key an MD5 of a configurable xlated string. This closes Bug #42
66 #ifdef HAVE_SYS_TYPES_H
67 #include <sys/types.h>
74 #ifdef HAVE_INTTYPES_H
78 #ifdef HAVE_NETINET_IN_H
79 #include <netinet/in.h>
85 #include "../../include/md5.h"
90 #ifdef NEEDS_GDBM_SYNC
91 # define GDBM_SYNCOPT GDBM_SYNC
93 # define GDBM_SYNCOPT 0
97 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
99 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
102 #define MAX_NAS_NAME_SIZE 64
104 static const char rcsid[] = "$Id$";
107 * Define a structure for our module configuration.
109 * These variables do not need to be in a structure, but it's
110 * a lot cleaner to do so, and a pointer to the structure can
111 * be used as the instance handle.
113 typedef struct rlm_ippool_t {
118 uint32_t range_start;
126 #ifdef HAVE_PTHREAD_H
127 pthread_mutex_t op_mutex;
131 #ifndef HAVE_PTHREAD_H
133 * This is easier than ifdef's throughout the code.
135 #define pthread_mutex_init(_x, _y)
136 #define pthread_mutex_destroy(_x)
137 #define pthread_mutex_lock(_x)
138 #define pthread_mutex_unlock(_x)
141 typedef struct ippool_info {
150 typedef struct ippool_key {
155 * A mapping of configuration file names to internal variables.
157 * Note that the string is dynamically allocated, so it MUST
158 * be freed. When the configuration file parse re-reads the string,
159 * it free's the old one, and strdup's the new one, placing the pointer
160 * to the strdup'd string into 'config.string'. This gets around
163 static CONF_PARSER module_config[] = {
164 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
165 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
166 { "key", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,key), NULL, "%{NAS-IP-Address} %{NAS-Port}" },
167 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
168 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
169 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
170 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
171 { "override", PW_TYPE_BOOLEAN, offsetof(rlm_ippool_t,override), NULL, "no" },
172 { "maximum-timeout", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,max_timeout), NULL, "0" },
173 { NULL, -1, 0, NULL, NULL }
177 * Do any per-module initialization that is separate to each
178 * configured instance of the module. e.g. set up connections
179 * to external databases, read configuration files, set up
180 * dictionary entries, etc.
182 * If configuration information is given in the config section
183 * that must be referenced in later calls, store a handle to it
184 * in *instance otherwise put a null pointer there.
186 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
196 const char *cli = "0";
197 char *pool_name = NULL;
200 * Set up a storage area for instance data
202 data = rad_malloc(sizeof(*data));
206 memset(data, 0, sizeof(*data));
209 * If the configuration parameters can't be parsed, then
212 if (cf_section_parse(conf, data, module_config) < 0) {
216 cache_size = data->cache_size;
218 if (data->session_db == NULL) {
219 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
223 if (data->ip_index == NULL) {
224 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
228 data->range_start = htonl(data->range_start);
229 data->range_stop = htonl(data->range_stop);
230 data->netmask = htonl(data->netmask);
231 if (data->range_start == 0 || data->range_stop == 0 || \
232 data->range_start >= data->range_stop ) {
233 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
238 data->gdbm = gdbm_open(data->session_db, sizeof(int),
239 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
240 if (data->gdbm == NULL) {
241 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
242 data->session_db, strerror(errno));
245 data->ip = gdbm_open(data->ip_index, sizeof(int),
246 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
247 if (data->ip == NULL) {
248 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
249 data->ip_index, strerror(errno));
252 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
253 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
254 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
255 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
257 key_datum = gdbm_firstkey(data->gdbm);
258 if (key_datum.dptr == NULL){
260 * If the database does not exist initialize it.
261 * We set the nas/port pairs to not existent values and
269 DEBUG("rlm_ippool: Initializing database");
270 for(i=data->range_start,j=~0;i<=data->range_stop;i++,j--){
273 * Net and Broadcast addresses are excluded
275 or_result = i | data->netmask;
276 if (~data->netmask != 0 &&
277 (or_result == data->netmask ||
278 (~or_result == 0))) {
279 DEBUG("rlm_ippool: IP %s excluded",
280 ip_ntoa(str, ntohl(i)));
284 sprintf(init_str,"%016d",j);
285 DEBUG("rlm_ippool: Initialized bucket: %s",init_str);
286 memcpy(key.key, init_str,16);
287 key_datum.dptr = (char *) &key;
288 key_datum.dsize = sizeof(ippool_key);
290 entry.ipaddr = ntohl(i);
295 strcpy(entry.cli,cli);
297 data_datum.dptr = (char *) &entry;
298 data_datum.dsize = sizeof(ippool_info);
300 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
302 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
303 data->session_db, gdbm_strerror(gdbm_errno));
305 gdbm_close(data->gdbm);
306 gdbm_close(data->ip);
312 free(key_datum.dptr);
314 /* Add the ip pool name */
316 pool_name = cf_section_name2(conf);
317 if (pool_name != NULL)
318 data->name = strdup(pool_name);
320 pthread_mutex_init(&data->op_mutex, NULL);
328 * Check for an Accounting-Stop
329 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
331 static int ippool_accounting(void *instance, REQUEST *request)
333 rlm_ippool_t *data = (rlm_ippool_t *)instance;
337 int acctstatustype = 0;
346 char xlat_str[MAX_STRING_LEN];
350 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
351 acctstatustype = vp->lvalue;
353 DEBUG("rlm_ippool: Could not find account status type in packet. Return NOOP.");
354 return RLM_MODULE_NOOP;
356 switch(acctstatustype){
358 if (!radius_xlat(xlat_str,MAX_STRING_LEN,data->key, request, NULL)){
359 DEBUG("rlm_ippool: xlat on the 'key' directive failed");
360 return RLM_MODULE_NOOP;
362 MD5Init(&md5_context);
363 MD5Update(&md5_context, xlat_str, strlen(xlat_str));
364 MD5Final(key_str, &md5_context);
366 lrad_bin2hex(key_str,hex_str,16);
368 DEBUG("rlm_ippool: MD5 on 'key' directive maps to: %s",hex_str);
369 memcpy(key.key,key_str,16);
372 /* We don't care about any other accounting packet */
373 DEBUG("rlm_ippool: This is not an Accounting-Stop. Return NOOP.");
375 return RLM_MODULE_NOOP;
378 DEBUG("rlm_ippool: Searching for an entry for key: '%s'",xlat_str);
379 key_datum.dptr = (char *) &key;
380 key_datum.dsize = sizeof(ippool_key);
382 pthread_mutex_lock(&data->op_mutex);
383 data_datum = gdbm_fetch(data->gdbm, key_datum);
384 if (data_datum.dptr != NULL){
387 * If the entry was found set active to zero
389 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
390 free(data_datum.dptr);
391 DEBUG("rlm_ippool: Deallocated entry for ip: %s",ip_ntoa(str,entry.ipaddr));
397 * Save the reference to the entry
399 save_datum.dptr = key_datum.dptr;
400 save_datum.dsize = key_datum.dsize;
402 data_datum.dptr = (char *) &entry;
403 data_datum.dsize = sizeof(ippool_info);
405 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
407 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
408 data->session_db, gdbm_strerror(gdbm_errno));
409 pthread_mutex_unlock(&data->op_mutex);
410 return RLM_MODULE_FAIL;
414 * Decrease allocated count from the ip index
416 key_datum.dptr = (char *) &entry.ipaddr;
417 key_datum.dsize = sizeof(uint32_t);
418 data_datum = gdbm_fetch(data->ip, key_datum);
419 if (data_datum.dptr != NULL){
420 memcpy(&num, data_datum.dptr, sizeof(int));
421 free(data_datum.dptr);
424 DEBUG("rlm_ippool: num: %d",num);
425 data_datum.dptr = (char *) #
426 data_datum.dsize = sizeof(int);
427 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
429 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
430 data->ip_index, gdbm_strerror(gdbm_errno));
431 pthread_mutex_unlock(&data->op_mutex);
432 return RLM_MODULE_FAIL;
434 if (num >0 && entry.extra == 1){
436 * We are doing MPPP and we still have nas/port entries referencing
437 * this ip. Delete this entry so that eventually we only keep one
438 * reference to this ip.
440 gdbm_delete(data->gdbm,save_datum);
444 pthread_mutex_unlock(&data->op_mutex);
447 pthread_mutex_unlock(&data->op_mutex);
448 DEBUG("rlm_ippool: Entry not found");
451 return RLM_MODULE_OK;
454 static int ippool_postauth(void *instance, REQUEST *request)
456 rlm_ippool_t *data = (rlm_ippool_t *) instance;
474 char xlat_str[MAX_STRING_LEN];
478 /* quiet the compiler */
482 /* Check if Pool-Name attribute exists. If it exists check our name and
483 * run only if they match
485 if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
486 if (data->name == NULL || (strcmp(data->name,vp->strvalue) && strcmp(vp->strvalue,"DEFAULT")))
487 return RLM_MODULE_NOOP;
489 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
490 return RLM_MODULE_NOOP;
497 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
501 if (!radius_xlat(xlat_str,MAX_STRING_LEN,data->key, request, NULL)){
502 DEBUG("rlm_ippool: xlat on the 'key' directive failed");
503 return RLM_MODULE_NOOP;
505 MD5Init(&md5_context);
506 MD5Update(&md5_context, xlat_str, strlen(xlat_str));
507 MD5Final(key_str, &md5_context);
509 lrad_bin2hex(key_str,hex_str,16);
511 DEBUG("rlm_ippool: MD5 on 'key' directive maps to: %s",hex_str);
512 memcpy(key.key,key_str,16);
514 DEBUG("rlm_ippool: Searching for an entry for key: '%s'",hex_str);
515 key_datum.dptr = (char *) &key;
516 key_datum.dsize = sizeof(ippool_key);
518 pthread_mutex_lock(&data->op_mutex);
519 data_datum = gdbm_fetch(data->gdbm, key_datum);
520 if (data_datum.dptr != NULL){
522 * If there is a corresponding entry in the database with active=1 it is stale.
526 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
527 free(data_datum.dptr);
529 DEBUG("rlm_ippool: Found a stale entry for ip: %s",ip_ntoa(str,entry.ipaddr));
535 * Save the reference to the entry
537 save_datum.dptr = key_datum.dptr;
538 save_datum.dsize = key_datum.dsize;
540 data_datum.dptr = (char *) &entry;
541 data_datum.dsize = sizeof(ippool_info);
543 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
545 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
546 data->session_db, gdbm_strerror(gdbm_errno));
547 pthread_mutex_unlock(&data->op_mutex);
548 return RLM_MODULE_FAIL;
550 /* Decrease allocated count from the ip index */
552 key_datum.dptr = (char *) &entry.ipaddr;
553 key_datum.dsize = sizeof(uint32_t);
554 data_datum = gdbm_fetch(data->ip, key_datum);
555 if (data_datum.dptr != NULL){
556 memcpy(&num, data_datum.dptr, sizeof(int));
557 free(data_datum.dptr);
560 DEBUG("rlm_ippool: num: %d",num);
561 data_datum.dptr = (char *) #
562 data_datum.dsize = sizeof(int);
563 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
565 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
566 data->ip_index, gdbm_strerror(gdbm_errno));
567 pthread_mutex_unlock(&data->op_mutex);
568 return RLM_MODULE_FAIL;
570 if (num >0 && entry.extra == 1){
572 * We are doing MPPP and we still have nas/port entries referencing
573 * this ip. Delete this entry so that eventually we only keep one
574 * reference to this ip.
576 gdbm_delete(data->gdbm,save_datum);
583 pthread_mutex_unlock(&data->op_mutex);
586 * If there is a Framed-IP-Address attribute in the reply, check for override
588 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
589 DEBUG("rlm_ippool: Found Framed-IP-Address attribute in reply attribute list.");
592 /* Override supplied Framed-IP-Address */
593 DEBUG("rlm_ippool: override is set to yes. Override the existing Framed-IP-Address attribute.");
594 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
597 DEBUG("rlm_ippool: override is set to no. Return NOOP.");
598 return RLM_MODULE_NOOP;
603 * Walk through the database searching for an active=0 entry.
604 * We search twice. Once to see if we have an active entry with the same callerid
605 * so that MPPP can work ok and then once again to find a free entry.
608 pthread_mutex_lock(&data->op_mutex);
610 key_datum.dptr = NULL;
612 key_datum = gdbm_firstkey(data->gdbm);
613 while(key_datum.dptr){
614 data_datum = gdbm_fetch(data->gdbm, key_datum);
615 if (data_datum.dptr){
616 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
617 free(data_datum.dptr);
619 * If we find an entry for the same caller-id with active=1
620 * then we use that for multilink (MPPP) to work properly.
622 if (strcmp(entry.cli,cli) == 0 && entry.active){
627 nextkey = gdbm_nextkey(data->gdbm, key_datum);
628 free(key_datum.dptr);
633 if (key_datum.dptr == NULL){
634 key_datum = gdbm_firstkey(data->gdbm);
635 while(key_datum.dptr){
636 data_datum = gdbm_fetch(data->gdbm, key_datum);
637 if (data_datum.dptr){
638 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
639 free(data_datum.dptr);
642 * Find an entry with active == 0
643 * or an entry that has expired
645 if (entry.active == 0 || (entry.timestamp && ((entry.timeout &&
646 request->timestamp >= (entry.timestamp + entry.timeout)) ||
647 (data->max_timeout && request->timestamp >= (entry.timestamp + data->max_timeout))))){
650 tmp.dptr = (char *) &entry.ipaddr;
651 tmp.dsize = sizeof(uint32_t);
652 data_datum = gdbm_fetch(data->ip, tmp);
655 * If we find an entry in the ip index and the number is zero (meaning
656 * that we haven't allocated the same ip address to another nas/port pair)
657 * or if we don't find an entry then delete the session entry so
658 * that we can change the key
659 * Else we don't delete the session entry since we haven't yet deallocated the
660 * corresponding ip address and we continue our search.
663 if (data_datum.dptr){
664 memcpy(&num,data_datum.dptr, sizeof(int));
665 free(data_datum.dptr);
677 nextkey = gdbm_nextkey(data->gdbm, key_datum);
678 free(key_datum.dptr);
683 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
685 * We keep the operation mutex locked until after we have set the corresponding entry active
690 * Found == 1 means we have the nas/port combination entry in our database
691 * We exchange the ip address between the nas/port entry and the free entry
692 * Afterwards we will save the free ip address to the nas/port entry.
694 * ---------------------------------------------
695 * - NAS/PORT Entry |||| Free Entry ||| Time
696 * - IP1 IP2(Free) BEFORE
697 * - IP2(Free) IP1 AFTER
698 * ---------------------------------------------
700 * We only do this if we are NOT doing MPPP
704 datum data_datum_tmp;
707 memcpy(key_tmp.key,key_str,16);
708 key_datum_tmp.dptr = (char *) &key_tmp;
709 key_datum_tmp.dsize = sizeof(ippool_key);
711 data_datum_tmp = gdbm_fetch(data->gdbm, key_datum_tmp);
712 if (data_datum_tmp.dptr != NULL){
714 rcode = gdbm_store(data->gdbm, key_datum, data_datum_tmp, GDBM_REPLACE);
716 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
717 data->session_db, gdbm_strerror(gdbm_errno));
718 pthread_mutex_unlock(&data->op_mutex);
719 return RLM_MODULE_FAIL;
721 free(data_datum_tmp.dptr);
726 * We have not found the nas/port combination
730 * Delete the entry so that we can change the key
731 * All is well. We delete one entry and we add one entry
733 gdbm_delete(data->gdbm, key_datum);
737 * We are doing MPPP. (mppp should be 1)
738 * We don't do anything.
739 * We will create an extra not needed entry in the database in this case
740 * but we don't really care since we always also use the ip_index database
741 * when we search for a free entry.
742 * We will also delete that entry on the accounting section so that we only
743 * have one nas/port entry referencing each ip
748 radlog(L_ERR, "rlm_ippool: mppp is not one. Please report this behaviour.");
751 free(key_datum.dptr);
753 entry.timestamp = request->timestamp;
754 if ((vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL)
755 entry.timeout = (time_t) vp->lvalue;
760 data_datum.dptr = (char *) &entry;
761 data_datum.dsize = sizeof(ippool_info);
762 memcpy(key.key, key_str, 16);
763 key_datum.dptr = (char *) &key;
764 key_datum.dsize = sizeof(ippool_key);
766 DEBUG2("rlm_ippool: Allocating ip to key: '%s'",hex_str);
767 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
769 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
770 data->session_db, gdbm_strerror(gdbm_errno));
771 pthread_mutex_unlock(&data->op_mutex);
772 return RLM_MODULE_FAIL;
775 /* Increase the ip index count */
776 key_datum.dptr = (char *) &entry.ipaddr;
777 key_datum.dsize = sizeof(uint32_t);
778 data_datum = gdbm_fetch(data->ip, key_datum);
779 if (data_datum.dptr){
780 memcpy(&num,data_datum.dptr,sizeof(int));
781 free(data_datum.dptr);
785 DEBUG("rlm_ippool: num: %d",num);
786 data_datum.dptr = (char *) #
787 data_datum.dsize = sizeof(int);
788 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
790 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
791 data->ip_index, gdbm_strerror(gdbm_errno));
792 pthread_mutex_unlock(&data->op_mutex);
793 return RLM_MODULE_FAIL;
795 pthread_mutex_unlock(&data->op_mutex);
798 DEBUG("rlm_ippool: Allocated ip %s to client key: %s",ip_ntoa(str,entry.ipaddr),hex_str);
799 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
800 radlog(L_ERR|L_CONS, "no memory");
801 return RLM_MODULE_FAIL;
803 vp->lvalue = entry.ipaddr;
804 ip_ntoa(vp->strvalue, vp->lvalue);
805 pairadd(&request->reply->vps, vp);
808 * If there is no Framed-Netmask attribute in the
811 if (pairfind(request->reply->vps, PW_FRAMED_IP_NETMASK) == NULL) {
812 if ((vp = paircreate(PW_FRAMED_IP_NETMASK, PW_TYPE_IPADDR)) == NULL)
813 radlog(L_ERR|L_CONS, "no memory");
815 vp->lvalue = ntohl(data->netmask);
816 ip_ntoa(vp->strvalue, vp->lvalue);
817 pairadd(&request->reply->vps, vp);
823 pthread_mutex_unlock(&data->op_mutex);
824 DEBUG("rlm_ippool: No available ip addresses in pool.");
825 return RLM_MODULE_NOTFOUND;
828 return RLM_MODULE_OK;
831 static int ippool_detach(void *instance)
833 rlm_ippool_t *data = (rlm_ippool_t *) instance;
835 gdbm_close(data->gdbm);
836 gdbm_close(data->ip);
837 free(data->session_db);
839 free(data->ip_index);
840 pthread_mutex_destroy(&data->op_mutex);
847 * The module name should be the only globally exported symbol.
848 * That is, everything else should be 'static'.
850 * If the module needs to temporarily modify it's instantiation
851 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
852 * The server will then take care of ensuring that the module
853 * is single-threaded.
855 module_t rlm_ippool = {
857 RLM_TYPE_THREAD_SAFE, /* type */
858 NULL, /* initialization */
859 ippool_instantiate, /* instantiation */
861 NULL, /* authentication */
862 NULL, /* authorization */
863 NULL, /* preaccounting */
864 ippool_accounting, /* accounting */
865 NULL, /* checksimul */
866 NULL, /* pre-proxy */
867 NULL, /* post-proxy */
868 ippool_postauth /* post-auth */
870 ippool_detach, /* detach */