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.
64 #ifdef HAVE_SYS_TYPES_H
65 #include <sys/types.h>
72 #ifdef HAVE_INTTYPES_H
76 #ifdef HAVE_NETINET_IN_H
77 #include <netinet/in.h>
87 #ifdef NEEDS_GDBM_SYNC
88 # define GDBM_SYNCOPT GDBM_SYNC
90 # define GDBM_SYNCOPT 0
94 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
96 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
99 #define MAX_NAS_NAME_SIZE 64
101 static const char rcsid[] = "$Id$";
104 * Define a structure for our module configuration.
106 * These variables do not need to be in a structure, but it's
107 * a lot cleaner to do so, and a pointer to the structure can
108 * be used as the instance handle.
110 typedef struct rlm_ippool_t {
114 uint32_t range_start;
122 #ifdef HAVE_PTHREAD_H
123 pthread_mutex_t op_mutex;
127 #ifndef HAVE_PTHREAD_H
129 * This is easier than ifdef's throughout the code.
131 #define pthread_mutex_init(_x, _y)
132 #define pthread_mutex_destroy(_x)
133 #define pthread_mutex_lock(_x)
134 #define pthread_mutex_unlock(_x)
137 typedef struct ippool_info {
146 typedef struct ippool_key {
147 char nas[MAX_NAS_NAME_SIZE];
152 * A mapping of configuration file names to internal variables.
154 * Note that the string is dynamically allocated, so it MUST
155 * be freed. When the configuration file parse re-reads the string,
156 * it free's the old one, and strdup's the new one, placing the pointer
157 * to the strdup'd string into 'config.string'. This gets around
160 static CONF_PARSER module_config[] = {
161 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
162 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
163 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
164 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
165 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
166 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
167 { "override", PW_TYPE_BOOLEAN, offsetof(rlm_ippool_t,override), NULL, "no" },
168 { "maximum-timeout", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,max_timeout), NULL, "0" },
169 { NULL, -1, 0, NULL, NULL }
174 * Do any per-module initialization that is separate to each
175 * configured instance of the module. e.g. set up connections
176 * to external databases, read configuration files, set up
177 * dictionary entries, etc.
179 * If configuration information is given in the config section
180 * that must be referenced in later calls, store a handle to it
181 * in *instance otherwise put a null pointer there.
183 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
193 const char *cli = "0";
194 char *pool_name = NULL;
197 * Set up a storage area for instance data
199 data = rad_malloc(sizeof(*data));
203 memset(data, 0, sizeof(*data));
206 * If the configuration parameters can't be parsed, then
209 if (cf_section_parse(conf, data, module_config) < 0) {
213 cache_size = data->cache_size;
215 if (data->session_db == NULL) {
216 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
220 if (data->ip_index == NULL) {
221 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
225 data->range_start = htonl(data->range_start);
226 data->range_stop = htonl(data->range_stop);
227 data->netmask = htonl(data->netmask);
228 if (data->range_start == 0 || data->range_stop == 0 || \
229 data->range_start >= data->range_stop ) {
230 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
235 data->gdbm = gdbm_open(data->session_db, sizeof(int),
236 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
237 if (data->gdbm == NULL) {
238 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
239 data->session_db, strerror(errno));
242 data->ip = gdbm_open(data->ip_index, sizeof(int),
243 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
244 if (data->ip == NULL) {
245 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
246 data->ip_index, strerror(errno));
249 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
250 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
251 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
252 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
254 key_datum = gdbm_firstkey(data->gdbm);
255 if (key_datum.dptr == NULL){
257 * If the database does not exist initialize it.
258 * We set the nas/port pairs to not existent values and
264 const char *nas_init = "NOT_EXIST";
266 DEBUG("rlm_ippool: Initializing database");
267 for(i=data->range_start,j=~0;i<=data->range_stop;i++,j--){
270 * Net and Broadcast addresses are excluded
272 or_result = i | data->netmask;
273 if (~data->netmask != 0 &&
274 (or_result == data->netmask ||
275 (~or_result == 0))) {
276 DEBUG("rlm_ippool: IP %s excluded",
277 ip_ntoa(str, ntohl(i)));
281 strcpy(key.nas, nas_init);
283 key_datum.dptr = (char *) &key;
284 key_datum.dsize = sizeof(ippool_key);
286 entry.ipaddr = ntohl(i);
291 strcpy(entry.cli,cli);
293 data_datum.dptr = (char *) &entry;
294 data_datum.dsize = sizeof(ippool_info);
296 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
298 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
299 data->session_db, gdbm_strerror(gdbm_errno));
301 gdbm_close(data->gdbm);
302 gdbm_close(data->ip);
308 free(key_datum.dptr);
310 /* Add the ip pool name */
312 pool_name = cf_section_name2(conf);
313 if (pool_name != NULL)
314 data->name = strdup(pool_name);
316 pthread_mutex_init(&data->op_mutex, NULL);
324 * Check for an Accounting-Stop
325 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
327 static int ippool_accounting(void *instance, REQUEST *request)
329 rlm_ippool_t *data = (rlm_ippool_t *)instance;
333 int acctstatustype = 0;
334 unsigned int port = ~0;
336 char nas[MAX_NAS_NAME_SIZE];
344 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
345 acctstatustype = vp->lvalue;
347 DEBUG("rlm_ippool: Could not find account status type in packet. Return NOOP.");
348 return RLM_MODULE_NOOP;
350 switch(acctstatustype){
352 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
355 DEBUG("rlm_ippool: Could not find port number in packet. Return NOOP.");
356 return RLM_MODULE_NOOP;
358 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
359 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
361 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
362 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
364 DEBUG("rlm_ippool: Could not find nas information in packet. Return NOOP.");
365 return RLM_MODULE_NOOP;
370 /* We don't care about any other accounting packet */
371 DEBUG("rlm_ippool: This is not an Accounting-Stop. Return NOOP.");
373 return RLM_MODULE_NOOP;
376 memset(key.nas,0,MAX_NAS_NAME_SIZE);
377 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
379 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key.nas,key.port);
380 key_datum.dptr = (char *) &key;
381 key_datum.dsize = sizeof(ippool_key);
383 pthread_mutex_lock(&data->op_mutex);
384 data_datum = gdbm_fetch(data->gdbm, key_datum);
385 if (data_datum.dptr != NULL){
388 * If the entry was found set active to zero
390 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
391 free(data_datum.dptr);
392 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%u",ip_ntoa(str,entry.ipaddr),port);
398 * Save the reference to the entry
400 save_datum.dptr = key_datum.dptr;
401 save_datum.dsize = key_datum.dsize;
403 data_datum.dptr = (char *) &entry;
404 data_datum.dsize = sizeof(ippool_info);
406 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
408 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
409 data->session_db, gdbm_strerror(gdbm_errno));
410 pthread_mutex_unlock(&data->op_mutex);
411 return RLM_MODULE_FAIL;
415 * Decrease allocated count from the ip index
417 key_datum.dptr = (char *) &entry.ipaddr;
418 key_datum.dsize = sizeof(uint32_t);
419 data_datum = gdbm_fetch(data->ip, key_datum);
420 if (data_datum.dptr != NULL){
421 memcpy(&num, data_datum.dptr, sizeof(int));
422 free(data_datum.dptr);
425 DEBUG("rlm_ippool: num: %d",num);
426 data_datum.dptr = (char *) #
427 data_datum.dsize = sizeof(int);
428 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
430 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
431 data->ip_index, gdbm_strerror(gdbm_errno));
432 pthread_mutex_unlock(&data->op_mutex);
433 return RLM_MODULE_FAIL;
435 if (num >0 && entry.extra == 1){
437 * We are doing MPPP and we still have nas/port entries referencing
438 * this ip. Delete this entry so that eventually we only keep one
439 * reference to this ip.
441 gdbm_delete(data->gdbm,save_datum);
445 pthread_mutex_unlock(&data->op_mutex);
448 pthread_mutex_unlock(&data->op_mutex);
449 DEBUG("rlm_ippool: Entry not found");
452 return RLM_MODULE_OK;
455 static int ippool_postauth(void *instance, REQUEST *request)
457 rlm_ippool_t *data = (rlm_ippool_t *) instance;
458 unsigned int port = 0;
465 char nas[MAX_NAS_NAME_SIZE];
477 /* quiet the compiler */
481 /* Check if Pool-Name attribute exists. If it exists check our name and
482 * run only if they match
484 if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
485 if (data->name == NULL || (strcmp(data->name,vp->strvalue) && strcmp(vp->strvalue,"DEFAULT")))
486 return RLM_MODULE_NOOP;
488 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
489 return RLM_MODULE_NOOP;
493 * Get the nas ip address
496 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
497 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
499 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
500 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
502 DEBUG("rlm_ippool: Could not find nas information. Return NOOP.");
503 return RLM_MODULE_NOOP;
510 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
517 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT)) != NULL)
520 DEBUG("rlm_ippool: Could not find nas port information. Return NOOP.");
521 return RLM_MODULE_NOOP;
524 memset(key.nas,0,MAX_NAS_NAME_SIZE);
525 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
527 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key.nas,key.port);
528 key_datum.dptr = (char *) &key;
529 key_datum.dsize = sizeof(ippool_key);
531 pthread_mutex_lock(&data->op_mutex);
532 data_datum = gdbm_fetch(data->gdbm, key_datum);
533 if (data_datum.dptr != NULL){
535 * If there is a corresponding entry in the database with active=1 it is stale.
539 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
540 free(data_datum.dptr);
542 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%u",ip_ntoa(str,entry.ipaddr),port);
548 * Save the reference to the entry
550 save_datum.dptr = key_datum.dptr;
551 save_datum.dsize = key_datum.dsize;
553 data_datum.dptr = (char *) &entry;
554 data_datum.dsize = sizeof(ippool_info);
556 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
558 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
559 data->session_db, gdbm_strerror(gdbm_errno));
560 pthread_mutex_unlock(&data->op_mutex);
561 return RLM_MODULE_FAIL;
563 /* Decrease allocated count from the ip index */
565 key_datum.dptr = (char *) &entry.ipaddr;
566 key_datum.dsize = sizeof(uint32_t);
567 data_datum = gdbm_fetch(data->ip, key_datum);
568 if (data_datum.dptr != NULL){
569 memcpy(&num, data_datum.dptr, sizeof(int));
570 free(data_datum.dptr);
573 DEBUG("rlm_ippool: num: %d",num);
574 data_datum.dptr = (char *) #
575 data_datum.dsize = sizeof(int);
576 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
578 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
579 data->ip_index, gdbm_strerror(gdbm_errno));
580 pthread_mutex_unlock(&data->op_mutex);
581 return RLM_MODULE_FAIL;
583 if (num >0 && entry.extra == 1){
585 * We are doing MPPP and we still have nas/port entries referencing
586 * this ip. Delete this entry so that eventually we only keep one
587 * reference to this ip.
589 gdbm_delete(data->gdbm,save_datum);
596 pthread_mutex_unlock(&data->op_mutex);
599 * If there is a Framed-IP-Address attribute in the reply, check for override
601 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL) {
602 DEBUG("rlm_ippool: Found Framed-IP-Address attribute in reply attribute list.");
605 /* Override supplied Framed-IP-Address */
606 DEBUG("rlm_ippool: override is set to yes. Override the existing Framed-IP-Address attribute.");
607 pairdelete(&request->reply->vps, PW_FRAMED_IP_ADDRESS);
610 DEBUG("rlm_ippool: override is set to no. Return NOOP.");
611 return RLM_MODULE_NOOP;
616 * Walk through the database searching for an active=0 entry.
617 * We search twice. Once to see if we have an active entry with the same callerid
618 * so that MPPP can work ok and then once again to find a free entry.
621 pthread_mutex_lock(&data->op_mutex);
623 key_datum.dptr = NULL;
625 key_datum = gdbm_firstkey(data->gdbm);
626 while(key_datum.dptr){
627 data_datum = gdbm_fetch(data->gdbm, key_datum);
628 if (data_datum.dptr){
629 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
630 free(data_datum.dptr);
632 * If we find an entry for the same caller-id and nas with active=1
633 * then we use that for multilink (MPPP) to work properly.
635 if (strcmp(entry.cli,cli) == 0 && entry.active){
636 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
637 if (!strcmp(key.nas,nas)){
643 nextkey = gdbm_nextkey(data->gdbm, key_datum);
644 free(key_datum.dptr);
649 if (key_datum.dptr == NULL){
650 key_datum = gdbm_firstkey(data->gdbm);
651 while(key_datum.dptr){
652 data_datum = gdbm_fetch(data->gdbm, key_datum);
653 if (data_datum.dptr){
654 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
655 free(data_datum.dptr);
658 * Find an entry with active == 0
659 * or an entry that has expired
661 if (entry.active == 0 || (entry.timestamp && ((entry.timeout &&
662 request->timestamp >= (entry.timestamp + entry.timeout)) ||
663 (data->max_timeout && request->timestamp >= (entry.timestamp + data->max_timeout))))){
666 tmp.dptr = (char *) &entry.ipaddr;
667 tmp.dsize = sizeof(uint32_t);
668 data_datum = gdbm_fetch(data->ip, tmp);
671 * If we find an entry in the ip index and the number is zero (meaning
672 * that we haven't allocated the same ip address to another nas/port pair)
673 * or if we don't find an entry then delete the session entry so
674 * that we can change the key (nas/port)
675 * Else we don't delete the session entry since we haven't yet deallocated the
676 * corresponding ip address and we continue our search.
679 if (data_datum.dptr){
680 memcpy(&num,data_datum.dptr, sizeof(int));
681 free(data_datum.dptr);
693 nextkey = gdbm_nextkey(data->gdbm, key_datum);
694 free(key_datum.dptr);
699 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
701 * We keep the operation mutex locked until after we have set the corresponding entry active
706 * Found == 1 means we have the nas/port combination entry in our database
707 * We exchange the ip address between the nas/port entry and the free entry
708 * Afterwards we will save the free ip address to the nas/port entry.
710 * ---------------------------------------------
711 * - NAS/PORT Entry |||| Free Entry ||| Time
712 * - IP1 IP2(Free) BEFORE
713 * - IP2(Free) IP1 AFTER
714 * ---------------------------------------------
716 * We only do this if we are NOT doing MPPP
720 datum data_datum_tmp;
723 memset(key_tmp.nas,0,MAX_NAS_NAME_SIZE);
724 strncpy(key_tmp.nas,nas,MAX_NAS_NAME_SIZE -1 );
726 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%u",key_tmp.nas,key_tmp.port);
727 key_datum_tmp.dptr = (char *) &key_tmp;
728 key_datum_tmp.dsize = sizeof(ippool_key);
730 data_datum_tmp = gdbm_fetch(data->gdbm, key_datum_tmp);
731 if (data_datum_tmp.dptr != NULL){
733 rcode = gdbm_store(data->gdbm, key_datum, data_datum_tmp, GDBM_REPLACE);
735 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
736 data->session_db, gdbm_strerror(gdbm_errno));
737 pthread_mutex_unlock(&data->op_mutex);
738 return RLM_MODULE_FAIL;
740 free(data_datum_tmp.dptr);
745 * We have not found the nas/port combination
749 * Delete the entry so that we can change the key
750 * All is well. We delete one entry and we add one entry
752 gdbm_delete(data->gdbm, key_datum);
756 * We are doing MPPP. (mppp should be 1)
757 * We don't do anything.
758 * We will create an extra not needed entry in the database in this case
759 * but we don't really care since we always also use the ip_index database
760 * when we search for a free entry.
761 * We will also delete that entry on the accounting section so that we only
762 * have one nas/port entry referencing each ip
767 radlog(L_ERR, "rlm_ippool: mppp is not one. Please report this behaviour.");
770 free(key_datum.dptr);
772 entry.timestamp = request->timestamp;
773 if ((vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL)
774 entry.timeout = (time_t) vp->lvalue;
779 data_datum.dptr = (char *) &entry;
780 data_datum.dsize = sizeof(ippool_info);
781 memset(key.nas,0,MAX_NAS_NAME_SIZE);
782 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
784 key_datum.dptr = (char *) &key;
785 key_datum.dsize = sizeof(ippool_key);
787 DEBUG2("rlm_ippool: Allocating ip to nas/port: %s/%u",key.nas,key.port);
788 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
790 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
791 data->session_db, gdbm_strerror(gdbm_errno));
792 pthread_mutex_unlock(&data->op_mutex);
793 return RLM_MODULE_FAIL;
796 /* Increase the ip index count */
797 key_datum.dptr = (char *) &entry.ipaddr;
798 key_datum.dsize = sizeof(uint32_t);
799 data_datum = gdbm_fetch(data->ip, key_datum);
800 if (data_datum.dptr){
801 memcpy(&num,data_datum.dptr,sizeof(int));
802 free(data_datum.dptr);
806 DEBUG("rlm_ippool: num: %d",num);
807 data_datum.dptr = (char *) #
808 data_datum.dsize = sizeof(int);
809 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
811 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
812 data->ip_index, gdbm_strerror(gdbm_errno));
813 pthread_mutex_unlock(&data->op_mutex);
814 return RLM_MODULE_FAIL;
816 pthread_mutex_unlock(&data->op_mutex);
819 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %u",ip_ntoa(str,entry.ipaddr),
821 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
822 radlog(L_ERR|L_CONS, "no memory");
823 return RLM_MODULE_FAIL;
825 vp->lvalue = entry.ipaddr;
826 ip_ntoa(vp->strvalue, vp->lvalue);
827 pairadd(&request->reply->vps, vp);
830 * If there is no Framed-Netmask attribute in the
833 if (pairfind(request->reply->vps, PW_FRAMED_IP_NETMASK) == NULL) {
834 if ((vp = paircreate(PW_FRAMED_IP_NETMASK, PW_TYPE_IPADDR)) == NULL)
835 radlog(L_ERR|L_CONS, "no memory");
837 vp->lvalue = ntohl(data->netmask);
838 ip_ntoa(vp->strvalue, vp->lvalue);
839 pairadd(&request->reply->vps, vp);
845 pthread_mutex_unlock(&data->op_mutex);
846 DEBUG("rlm_ippool: No available ip addresses in pool.");
847 return RLM_MODULE_NOTFOUND;
850 return RLM_MODULE_OK;
853 static int ippool_detach(void *instance)
855 rlm_ippool_t *data = (rlm_ippool_t *) instance;
857 gdbm_close(data->gdbm);
858 gdbm_close(data->ip);
859 free(data->session_db);
860 free(data->ip_index);
861 pthread_mutex_destroy(&data->op_mutex);
868 * The module name should be the only globally exported symbol.
869 * That is, everything else should be 'static'.
871 * If the module needs to temporarily modify it's instantiation
872 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
873 * The server will then take care of ensuring that the module
874 * is single-threaded.
876 module_t rlm_ippool = {
878 RLM_TYPE_THREAD_SAFE, /* type */
879 NULL, /* initialization */
880 ippool_instantiate, /* instantiation */
882 NULL, /* authentication */
883 NULL, /* authorization */
884 NULL, /* preaccounting */
885 ippool_accounting, /* accounting */
886 NULL, /* checksimul */
887 NULL, /* pre-proxy */
888 NULL, /* post-proxy */
889 ippool_postauth /* post-auth */
891 ippool_detach, /* detach */