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>
26 #include "libradius.h"
39 #include <netinet/in.h>
41 #ifdef NEEDS_GDBM_SYNC
42 # define GDBM_SYNCOPT GDBM_SYNC
44 # define GDBM_SYNCOPT 0
48 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
50 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
53 #ifndef HAVE_GDBM_FDESC
54 #define gdbm_fdesc(foo) (-1)
57 #define ALL_ONES 4294967295
59 static const char rcsid[] = "$Id$";
62 * Define a structure for our module configuration.
64 * These variables do not need to be in a structure, but it's
65 * a lot cleaner to do so, and a pointer to the structure can
66 * be used as the instance handle.
68 typedef struct rlm_ippool_t {
81 typedef struct ippool_info {
87 typedef struct ippool_key {
93 * A mapping of configuration file names to internal variables.
95 * Note that the string is dynamically allocated, so it MUST
96 * be freed. When the configuration file parse re-reads the string,
97 * it free's the old one, and strdup's the new one, placing the pointer
98 * to the strdup'd string into 'config.string'. This gets around
101 static CONF_PARSER module_config[] = {
102 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
103 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
104 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
105 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
106 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
107 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
108 { NULL, -1, 0, NULL, NULL }
113 * Do any per-module initialization that is separate to each
114 * configured instance of the module. e.g. set up connections
115 * to external databases, read configuration files, set up
116 * dictionary entries, etc.
118 * If configuration information is given in the config section
119 * that must be referenced in later calls, store a handle to it
120 * in *instance otherwise put a null pointer there.
122 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
134 * Set up a storage area for instance data
136 data = rad_malloc(sizeof(*data));
139 * If the configuration parameters can't be parsed, then
142 if (cf_section_parse(conf, data, module_config) < 0) {
146 cache_size = data->cache_size;
148 if (data->session_db == NULL) {
149 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
153 if (data->ip_index == NULL) {
154 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
158 data->range_start = htonl(data->range_start);
159 data->range_stop = htonl(data->range_stop);
160 data->netmask = htonl(data->netmask);
161 if (data->range_start == 0 || data->range_stop == 0 || \
162 data->range_start >= data->range_stop ) {
163 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
167 data->gdbm = gdbm_open(data->session_db, sizeof(int),
168 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
169 if (data->gdbm == NULL) {
170 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
171 data->session_db, strerror(errno));
174 if (data->fd >= 0) data->fd = gdbm_fdesc(data->gdbm);
176 data->ip = gdbm_open(data->ip_index, sizeof(int),
177 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
178 if (data->ip == NULL) {
179 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
180 data->ip_index, strerror(errno));
183 if (data->ip_fd >= 0) data->ip_fd = gdbm_fdesc(data->ip);
185 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
186 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
187 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
188 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
190 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
191 key_datum = gdbm_firstkey(data->gdbm);
192 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
193 if (key_datum.dptr == NULL){
195 * If the database does not exist initialize it.
196 * We set the nas/port pairs to not existent values and
203 DEBUG("rlm_ippool: Initializing database");
204 for(i=data->range_start,j=-1;i<=data->range_stop;i++,j--){
207 * Net and Broadcast addresses are excluded
209 or_result = i | data->netmask;
210 if (or_result == data->netmask || or_result == ALL_ONES){
211 DEBUG("rlm_ippool: IP %s exlcluded",ip_ntoa(str,ntohl(i)));
217 key_datum.dptr = (ippool_key *) &key;
218 key_datum.dsize = sizeof(ippool_key);
220 entry.ipaddr = ntohl(i);
222 strcpy(entry.cli,cli);
224 data_datum.dptr = (ippool_info *) &entry;
225 data_datum.dsize = sizeof(ippool_info);
227 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
228 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
229 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
231 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
232 data->session_db, gdbm_strerror(gdbm_errno));
234 gdbm_close(data->gdbm);
235 gdbm_close(data->ip);
241 free(key_datum.dptr);
250 * Check for an Accounting-Stop
251 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
253 static int ippool_accounting(void *instance, REQUEST *request)
255 rlm_ippool_t *data = (rlm_ippool_t *)instance;
258 int acctstatustype = 0;
269 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
270 acctstatustype = vp->lvalue;
272 DEBUG("rlm_ippool: Could not find account status type in packet.");
273 return RLM_MODULE_NOOP;
275 switch(acctstatustype){
277 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
280 DEBUG("rlm_ippool: Could not find port number in packet.");
281 return RLM_MODULE_NOOP;
283 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
286 DEBUG("rlm_ippool: Could not find nas ip address in packet.");
287 return RLM_MODULE_NOOP;
291 /* We don't care about any other accounting packet */
293 return RLM_MODULE_NOOP;
298 key_datum.dptr = (ippool_key *) &key;
299 key_datum.dsize = sizeof(ippool_key);
301 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
302 data_datum = gdbm_fetch(data->gdbm, key_datum);
303 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
304 if (data_datum.dptr != NULL){
307 * If the entry was found set active to zero
309 memcpy(&entry, data_datum.dptr, sizeof(int));
310 free(data_datum.dptr);
311 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
314 data_datum.dptr = (ippool_info *) &entry;
315 data_datum.dsize = sizeof(ippool_info);
317 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
318 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
319 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
321 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
322 data->session_db, gdbm_strerror(gdbm_errno));
323 return RLM_MODULE_FAIL;
327 * Decrease allocated count from the ip index
329 key_datum.dptr = (uint32_t *) &entry.ipaddr;
330 key_datum.dsize = sizeof(uint32_t);
331 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
332 data_datum = gdbm_fetch(data->ip, key_datum);
333 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
334 if (data_datum.dptr != NULL){
335 memcpy(&num, data_datum.dptr, sizeof(int));
336 free(data_datum.dptr);
339 DEBUG("rlm_ippool: num: %d",num);
340 data_datum.dptr = (int *) #
341 data_datum.dsize = sizeof(int);
342 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
343 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
344 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
346 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
347 data->ip_index, gdbm_strerror(gdbm_errno));
348 return RLM_MODULE_FAIL;
354 return RLM_MODULE_OK;
357 static int ippool_authorize(void *instance, REQUEST *request)
359 rlm_ippool_t *data = (rlm_ippool_t *) instance;
375 /* quiet the compiler */
380 * Get the nas ip address
382 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
385 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
392 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
398 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL){
403 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",ip_ntoa(str,nas),port);
404 key_datum.dptr = (ippool_key *) &key;
405 key_datum.dsize = sizeof(ippool_key);
407 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
408 data_datum = gdbm_fetch(data->gdbm, key_datum);
409 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
410 if (data_datum.dptr != NULL){
412 * If there is a corresponding entry in the database with active=1 it is stale.
415 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
416 free(data_datum.dptr);
418 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
421 data_datum.dptr = (ippool_info *) &entry;
422 data_datum.dsize = sizeof(ippool_info);
424 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
425 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
426 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
428 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
429 data->session_db, gdbm_strerror(gdbm_errno));
430 return RLM_MODULE_FAIL;
432 /* Decrease allocated count from the ip index */
434 key_datum.dptr = (uint32_t *) &entry.ipaddr;
435 key_datum.dsize = sizeof(uint32_t);
436 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
437 data_datum = gdbm_fetch(data->ip, key_datum);
438 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
439 if (data_datum.dptr != NULL){
440 memcpy(&num, data_datum.dptr, sizeof(int));
441 free(data_datum.dptr);
444 DEBUG("rlm_ippool: num: %d",num);
445 data_datum.dptr = (int *) #
446 data_datum.dsize = sizeof(int);
447 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
448 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
449 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
451 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
452 data->ip_index, gdbm_strerror(gdbm_errno));
453 return RLM_MODULE_FAIL;
461 * If there is a Framed-IP-Address attribute in the reply do nothing
463 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
464 return RLM_MODULE_NOOP;
467 * Walk through the database searching for an active=0 entry.
470 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
471 key_datum = gdbm_firstkey(data->gdbm);
472 while(key_datum.dptr){
473 data_datum = gdbm_fetch(data->gdbm, key_datum);
474 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
475 free(data_datum.dptr);
476 if (cli != NULL && strcmp(entry.cli,cli) == 0 && entry.active){
477 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
481 if (entry.active == 0){
484 tmp.dptr = (uint32_t *) &entry.ipaddr;
485 tmp.dsize = sizeof(uint32_t);
486 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
487 data_datum = gdbm_fetch(data->ip, tmp);
488 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
491 * If we find an entry in the ip index and the number is zero (meaning
492 * that we haven't allocated the same ip address to another nas/port pair)
493 * or if we don't find an entry then delete the session entry so
494 * that we can change the key (nas/port)
495 * Else we don't delete the session entry since we haven't yet deallocated the
496 * corresponding ip address and we continue our search.
499 if (data_datum.dptr){
500 memcpy(&num,data_datum.dptr, sizeof(int));
501 free(data_datum.dptr);
512 nextkey = gdbm_nextkey(data->gdbm, key_datum);
513 free(key_datum.dptr);
516 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
518 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
523 data_datum.dptr = (ippool_info *) &entry;
524 data_datum.dsize = sizeof(ippool_info);
528 * Delete the entry so that we can change the key
530 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
531 gdbm_delete(data->gdbm, key_datum);
532 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
534 free(key_datum.dptr);
537 key_datum.dptr = (ippool_key *) &key;
538 key_datum.dsize = sizeof(ippool_key);
540 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
541 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
542 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
544 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
545 data->session_db, gdbm_strerror(gdbm_errno));
546 return RLM_MODULE_FAIL;
549 /* Increase the ip index count */
550 key_datum.dptr = (uint32_t *) &entry.ipaddr;
551 key_datum.dsize = sizeof(uint32_t);
552 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
553 data_datum = gdbm_fetch(data->ip, key_datum);
554 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
555 if (data_datum.dptr){
556 memcpy(&num,data_datum.dptr,sizeof(int));
557 free(data_datum.dptr);
560 DEBUG("rlm_ippool: num: %d",num);
561 data_datum.dptr = (int *) #
562 data_datum.dsize = sizeof(int);
563 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
564 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
565 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
567 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
568 data->ip_index, gdbm_strerror(gdbm_errno));
569 return RLM_MODULE_FAIL;
573 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %d",ip_ntoa(str,entry.ipaddr),
574 ip_ntoa(str2,key.nas),port);
575 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
576 radlog(L_ERR|L_CONS, "no memory");
577 return RLM_MODULE_NOOP;
579 vp->lvalue = entry.ipaddr;
580 pairadd(&request->reply->vps, vp);
583 DEBUG("rlm_ippool: No available ip addresses in pool.");
584 return RLM_MODULE_NOOP;
587 return RLM_MODULE_OK;
590 static int ippool_detach(void *instance)
592 rlm_ippool_t *data = (rlm_ippool_t *) instance;
594 gdbm_close(data->gdbm);
595 gdbm_close(data->ip);
596 free(data->session_db);
597 free(data->ip_index);
604 * The module name should be the only globally exported symbol.
605 * That is, everything else should be 'static'.
607 * If the module needs to temporarily modify it's instantiation
608 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
609 * The server will then take care of ensuring that the module
610 * is single-threaded.
612 module_t rlm_ippool = {
614 RLM_TYPE_THREAD_SAFE, /* type */
615 NULL, /* initialization */
616 ippool_instantiate, /* instantiation */
618 NULL, /* authentication */
619 ippool_authorize, /* authorization */
620 NULL, /* preaccounting */
621 ippool_accounting, /* accounting */
622 NULL /* checksimul */
624 ippool_detach, /* detach */