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
35 #include "libradius.h"
48 #include <netinet/in.h>
50 #ifdef NEEDS_GDBM_SYNC
51 # define GDBM_SYNCOPT GDBM_SYNC
53 # define GDBM_SYNCOPT 0
57 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
59 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
62 #ifndef HAVE_GDBM_FDESC
63 #define gdbm_fdesc(foo) (-1)
66 #define ALL_ONES 4294967295
67 #define MAX_NAS_NAME_SIZE 64
69 static const char rcsid[] = "$Id$";
72 * Define a structure for our module configuration.
74 * These variables do not need to be in a structure, but it's
75 * a lot cleaner to do so, and a pointer to the structure can
76 * be used as the instance handle.
78 typedef struct rlm_ippool_t {
92 typedef struct ippool_info {
98 typedef struct ippool_key {
99 char nas[MAX_NAS_NAME_SIZE];
104 * A mapping of configuration file names to internal variables.
106 * Note that the string is dynamically allocated, so it MUST
107 * be freed. When the configuration file parse re-reads the string,
108 * it free's the old one, and strdup's the new one, placing the pointer
109 * to the strdup'd string into 'config.string'. This gets around
112 static CONF_PARSER module_config[] = {
113 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
114 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
115 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
116 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
117 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
118 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
119 { NULL, -1, 0, NULL, NULL }
124 * Do any per-module initialization that is separate to each
125 * configured instance of the module. e.g. set up connections
126 * to external databases, read configuration files, set up
127 * dictionary entries, etc.
129 * If configuration information is given in the config section
130 * that must be referenced in later calls, store a handle to it
131 * in *instance otherwise put a null pointer there.
133 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
142 const char *cli = "0";
143 char *pool_name = NULL;
146 * Set up a storage area for instance data
148 data = rad_malloc(sizeof(*data));
151 * If the configuration parameters can't be parsed, then
154 if (cf_section_parse(conf, data, module_config) < 0) {
158 cache_size = data->cache_size;
160 if (data->session_db == NULL) {
161 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
165 if (data->ip_index == NULL) {
166 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
170 data->range_start = htonl(data->range_start);
171 data->range_stop = htonl(data->range_stop);
172 data->netmask = htonl(data->netmask);
173 if (data->range_start == 0 || data->range_stop == 0 || \
174 data->range_start >= data->range_stop ) {
175 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
179 data->gdbm = gdbm_open(data->session_db, sizeof(int),
180 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
181 if (data->gdbm == NULL) {
182 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
183 data->session_db, strerror(errno));
186 if (data->fd >= 0) data->fd = gdbm_fdesc(data->gdbm);
188 data->ip = gdbm_open(data->ip_index, sizeof(int),
189 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
190 if (data->ip == NULL) {
191 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
192 data->ip_index, strerror(errno));
195 if (data->ip_fd >= 0) data->ip_fd = gdbm_fdesc(data->ip);
197 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
198 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
199 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
200 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
202 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
203 key_datum = gdbm_firstkey(data->gdbm);
204 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
205 if (key_datum.dptr == NULL){
207 * If the database does not exist initialize it.
208 * We set the nas/port pairs to not existent values and
214 const char *nas_init = "NOT_EXIST";
216 DEBUG("rlm_ippool: Initializing database");
217 for(i=data->range_start,j=-1;i<=data->range_stop;i++,j--){
220 * Net and Broadcast addresses are excluded
222 or_result = i | data->netmask;
223 if (or_result == data->netmask || or_result == ALL_ONES){
224 DEBUG("rlm_ippool: IP %s exlcluded",ip_ntoa(str,ntohl(i)));
228 strcpy(key.nas, nas_init);
230 key_datum.dptr = (ippool_key *) &key;
231 key_datum.dsize = sizeof(ippool_key);
233 entry.ipaddr = ntohl(i);
235 strcpy(entry.cli,cli);
237 data_datum.dptr = (ippool_info *) &entry;
238 data_datum.dsize = sizeof(ippool_info);
240 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
241 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
242 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
244 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
245 data->session_db, gdbm_strerror(gdbm_errno));
247 gdbm_close(data->gdbm);
248 gdbm_close(data->ip);
254 free(key_datum.dptr);
256 /* Add the ip pool name */
258 pool_name = cf_section_name2(conf);
259 if (pool_name != NULL)
260 data->name = strdup(pool_name);
269 * Check for an Accounting-Stop
270 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
272 static int ippool_accounting(void *instance, REQUEST *request)
274 rlm_ippool_t *data = (rlm_ippool_t *)instance;
277 int acctstatustype = 0;
280 char nas[MAX_NAS_NAME_SIZE];
288 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
289 acctstatustype = vp->lvalue;
291 DEBUG("rlm_ippool: Could not find account status type in packet.");
292 return RLM_MODULE_NOOP;
294 switch(acctstatustype){
296 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
299 DEBUG("rlm_ippool: Could not find port number in packet.");
300 return RLM_MODULE_NOOP;
302 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
303 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
305 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
306 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
308 DEBUG("rlm_ippool: Could not find nas information in packet.");
309 return RLM_MODULE_NOOP;
314 /* We don't care about any other accounting packet */
316 return RLM_MODULE_NOOP;
319 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
321 key_datum.dptr = (ippool_key *) &key;
322 key_datum.dsize = sizeof(ippool_key);
324 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
325 data_datum = gdbm_fetch(data->gdbm, key_datum);
326 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
327 if (data_datum.dptr != NULL){
330 * If the entry was found set active to zero
332 memcpy(&entry, data_datum.dptr, sizeof(int));
333 free(data_datum.dptr);
334 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
337 data_datum.dptr = (ippool_info *) &entry;
338 data_datum.dsize = sizeof(ippool_info);
340 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
341 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
342 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
344 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
345 data->session_db, gdbm_strerror(gdbm_errno));
346 return RLM_MODULE_FAIL;
350 * Decrease allocated count from the ip index
352 key_datum.dptr = (uint32_t *) &entry.ipaddr;
353 key_datum.dsize = sizeof(uint32_t);
354 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
355 data_datum = gdbm_fetch(data->ip, key_datum);
356 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
357 if (data_datum.dptr != NULL){
358 memcpy(&num, data_datum.dptr, sizeof(int));
359 free(data_datum.dptr);
362 DEBUG("rlm_ippool: num: %d",num);
363 data_datum.dptr = (int *) #
364 data_datum.dsize = sizeof(int);
365 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
366 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
367 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
369 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
370 data->ip_index, gdbm_strerror(gdbm_errno));
371 return RLM_MODULE_FAIL;
377 return RLM_MODULE_OK;
380 static int ippool_authorize(void *instance, REQUEST *request)
382 rlm_ippool_t *data = (rlm_ippool_t *) instance;
387 char nas[MAX_NAS_NAME_SIZE];
398 /* quiet the compiler */
402 /* Check if Pool-Name attribute exists. If it exists check our name and
403 * run only if they match
405 if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
406 if (data->name == NULL || strcmp(data->name,vp->strvalue))
407 return RLM_MODULE_NOOP;
409 DEBUG("rlm_ippool: Could not find Pool-Name attribute.");
410 return RLM_MODULE_NOOP;
414 * Get the nas ip address
416 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
417 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
419 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
420 strncpy(nas, vp->strvalue, MAX_NAS_NAME_SIZE - 1);
422 DEBUG("rlm_ippool: Could not find nas information.");
423 return RLM_MODULE_NOOP;
430 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
436 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL){
439 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE -1 );
441 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",nas,port);
442 key_datum.dptr = (ippool_key *) &key;
443 key_datum.dsize = sizeof(ippool_key);
445 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
446 data_datum = gdbm_fetch(data->gdbm, key_datum);
447 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
448 if (data_datum.dptr != NULL){
450 * If there is a corresponding entry in the database with active=1 it is stale.
453 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
454 free(data_datum.dptr);
456 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
459 data_datum.dptr = (ippool_info *) &entry;
460 data_datum.dsize = sizeof(ippool_info);
462 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
463 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
464 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
466 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
467 data->session_db, gdbm_strerror(gdbm_errno));
468 return RLM_MODULE_FAIL;
470 /* Decrease allocated count from the ip index */
472 key_datum.dptr = (uint32_t *) &entry.ipaddr;
473 key_datum.dsize = sizeof(uint32_t);
474 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
475 data_datum = gdbm_fetch(data->ip, key_datum);
476 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
477 if (data_datum.dptr != NULL){
478 memcpy(&num, data_datum.dptr, sizeof(int));
479 free(data_datum.dptr);
482 DEBUG("rlm_ippool: num: %d",num);
483 data_datum.dptr = (int *) #
484 data_datum.dsize = sizeof(int);
485 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
486 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
487 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
489 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
490 data->ip_index, gdbm_strerror(gdbm_errno));
491 return RLM_MODULE_FAIL;
499 * If there is a Framed-IP-Address attribute in the reply do nothing
501 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
502 return RLM_MODULE_NOOP;
505 * Walk through the database searching for an active=0 entry.
508 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
509 key_datum = gdbm_firstkey(data->gdbm);
510 while(key_datum.dptr){
511 data_datum = gdbm_fetch(data->gdbm, key_datum);
512 if (data_datum.dptr){
513 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
514 free(data_datum.dptr);
516 * If we find an entry for the same caller-id and nas with active=1
517 * then we use that for multilink (MPPP) to work properly.
519 if (cli != NULL && strcmp(entry.cli,cli) == 0 && entry.active){
520 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
521 if (!strcmp(key.nas,nas))
524 if (entry.active == 0){
527 tmp.dptr = (uint32_t *) &entry.ipaddr;
528 tmp.dsize = sizeof(uint32_t);
529 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
530 data_datum = gdbm_fetch(data->ip, tmp);
531 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
534 * If we find an entry in the ip index and the number is zero (meaning
535 * that we haven't allocated the same ip address to another nas/port pair)
536 * or if we don't find an entry then delete the session entry so
537 * that we can change the key (nas/port)
538 * Else we don't delete the session entry since we haven't yet deallocated the
539 * corresponding ip address and we continue our search.
542 if (data_datum.dptr){
543 memcpy(&num,data_datum.dptr, sizeof(int));
544 free(data_datum.dptr);
556 nextkey = gdbm_nextkey(data->gdbm, key_datum);
557 free(key_datum.dptr);
560 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
562 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
567 data_datum.dptr = (ippool_info *) &entry;
568 data_datum.dsize = sizeof(ippool_info);
572 * Delete the entry so that we can change the key
574 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
575 gdbm_delete(data->gdbm, key_datum);
576 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
578 free(key_datum.dptr);
579 strncpy(key.nas,nas,MAX_NAS_NAME_SIZE - 1);
581 key_datum.dptr = (ippool_key *) &key;
582 key_datum.dsize = sizeof(ippool_key);
584 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
585 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
586 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
588 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
589 data->session_db, gdbm_strerror(gdbm_errno));
590 return RLM_MODULE_FAIL;
593 /* Increase the ip index count */
594 key_datum.dptr = (uint32_t *) &entry.ipaddr;
595 key_datum.dsize = sizeof(uint32_t);
596 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
597 data_datum = gdbm_fetch(data->ip, key_datum);
598 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
599 if (data_datum.dptr){
600 memcpy(&num,data_datum.dptr,sizeof(int));
601 free(data_datum.dptr);
604 DEBUG("rlm_ippool: num: %d",num);
605 data_datum.dptr = (int *) #
606 data_datum.dsize = sizeof(int);
607 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
608 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
609 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
611 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
612 data->ip_index, gdbm_strerror(gdbm_errno));
613 return RLM_MODULE_FAIL;
617 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %d",ip_ntoa(str,entry.ipaddr),
619 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
620 radlog(L_ERR|L_CONS, "no memory");
621 return RLM_MODULE_NOOP;
623 vp->lvalue = entry.ipaddr;
624 pairadd(&request->reply->vps, vp);
627 DEBUG("rlm_ippool: No available ip addresses in pool.");
628 return RLM_MODULE_NOOP;
631 return RLM_MODULE_OK;
634 static int ippool_detach(void *instance)
636 rlm_ippool_t *data = (rlm_ippool_t *) instance;
638 gdbm_close(data->gdbm);
639 gdbm_close(data->ip);
640 free(data->session_db);
641 free(data->ip_index);
648 * The module name should be the only globally exported symbol.
649 * That is, everything else should be 'static'.
651 * If the module needs to temporarily modify it's instantiation
652 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
653 * The server will then take care of ensuring that the module
654 * is single-threaded.
656 module_t rlm_ippool = {
658 RLM_TYPE_THREAD_SAFE, /* type */
659 NULL, /* initialization */
660 ippool_instantiate, /* instantiation */
662 NULL, /* authentication */
663 ippool_authorize, /* authorization */
664 NULL, /* preaccounting */
665 ippool_accounting, /* accounting */
666 NULL, /* checksimul */
667 NULL, /* pre-proxy */
668 NULL, /* post-proxy */
671 ippool_detach, /* detach */