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
31 #include "libradius.h"
44 #include <netinet/in.h>
46 #ifdef NEEDS_GDBM_SYNC
47 # define GDBM_SYNCOPT GDBM_SYNC
49 # define GDBM_SYNCOPT 0
53 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
55 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
58 #ifndef HAVE_GDBM_FDESC
59 #define gdbm_fdesc(foo) (-1)
62 #define ALL_ONES 4294967295
64 static const char rcsid[] = "$Id$";
67 * Define a structure for our module configuration.
69 * These variables do not need to be in a structure, but it's
70 * a lot cleaner to do so, and a pointer to the structure can
71 * be used as the instance handle.
73 typedef struct rlm_ippool_t {
87 typedef struct ippool_info {
93 typedef struct ippool_key {
99 * A mapping of configuration file names to internal variables.
101 * Note that the string is dynamically allocated, so it MUST
102 * be freed. When the configuration file parse re-reads the string,
103 * it free's the old one, and strdup's the new one, placing the pointer
104 * to the strdup'd string into 'config.string'. This gets around
107 static CONF_PARSER module_config[] = {
108 { "session-db", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,session_db), NULL, NULL },
109 { "ip-index", PW_TYPE_STRING_PTR, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
110 { "range-start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
111 { "range-stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
112 { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
113 { "cache-size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
114 { NULL, -1, 0, NULL, NULL }
119 * Do any per-module initialization that is separate to each
120 * configured instance of the module. e.g. set up connections
121 * to external databases, read configuration files, set up
122 * dictionary entries, etc.
124 * If configuration information is given in the config section
125 * that must be referenced in later calls, store a handle to it
126 * in *instance otherwise put a null pointer there.
128 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
138 char *pool_name = NULL;
141 * Set up a storage area for instance data
143 data = rad_malloc(sizeof(*data));
146 * If the configuration parameters can't be parsed, then
149 if (cf_section_parse(conf, data, module_config) < 0) {
153 cache_size = data->cache_size;
155 if (data->session_db == NULL) {
156 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
160 if (data->ip_index == NULL) {
161 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
165 data->range_start = htonl(data->range_start);
166 data->range_stop = htonl(data->range_stop);
167 data->netmask = htonl(data->netmask);
168 if (data->range_start == 0 || data->range_stop == 0 || \
169 data->range_start >= data->range_stop ) {
170 radlog(L_ERR, "rlm_ippool: Invalid configuration data given.");
174 data->gdbm = gdbm_open(data->session_db, sizeof(int),
175 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
176 if (data->gdbm == NULL) {
177 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
178 data->session_db, strerror(errno));
181 if (data->fd >= 0) data->fd = gdbm_fdesc(data->gdbm);
183 data->ip = gdbm_open(data->ip_index, sizeof(int),
184 GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
185 if (data->ip == NULL) {
186 radlog(L_ERR, "rlm_ippool: Failed to open file %s: %s",
187 data->ip_index, strerror(errno));
190 if (data->ip_fd >= 0) data->ip_fd = gdbm_fdesc(data->ip);
192 if (gdbm_setopt(data->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
193 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
194 if (gdbm_setopt(data->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
195 radlog(L_ERR, "rlm_ippool: Failed to set cache size");
197 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
198 key_datum = gdbm_firstkey(data->gdbm);
199 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
200 if (key_datum.dptr == NULL){
202 * If the database does not exist initialize it.
203 * We set the nas/port pairs to not existent values and
210 DEBUG("rlm_ippool: Initializing database");
211 for(i=data->range_start,j=-1;i<=data->range_stop;i++,j--){
214 * Net and Broadcast addresses are excluded
216 or_result = i | data->netmask;
217 if (or_result == data->netmask || or_result == ALL_ONES){
218 DEBUG("rlm_ippool: IP %s exlcluded",ip_ntoa(str,ntohl(i)));
224 key_datum.dptr = (ippool_key *) &key;
225 key_datum.dsize = sizeof(ippool_key);
227 entry.ipaddr = ntohl(i);
229 strcpy(entry.cli,cli);
231 data_datum.dptr = (ippool_info *) &entry;
232 data_datum.dsize = sizeof(ippool_info);
234 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
235 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
236 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
238 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
239 data->session_db, gdbm_strerror(gdbm_errno));
241 gdbm_close(data->gdbm);
242 gdbm_close(data->ip);
248 free(key_datum.dptr);
250 /* Add the ip pool name */
252 pool_name = cf_section_name2(conf);
253 if (pool_name != NULL)
254 data->name = strdup(pool_name);
263 * Check for an Accounting-Stop
264 * If we find one and we have allocated an IP to this nas/port combination, deallocate it.
266 static int ippool_accounting(void *instance, REQUEST *request)
268 rlm_ippool_t *data = (rlm_ippool_t *)instance;
271 int acctstatustype = 0;
282 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
283 acctstatustype = vp->lvalue;
285 DEBUG("rlm_ippool: Could not find account status type in packet.");
286 return RLM_MODULE_NOOP;
288 switch(acctstatustype){
290 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
293 DEBUG("rlm_ippool: Could not find port number in packet.");
294 return RLM_MODULE_NOOP;
296 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
299 DEBUG("rlm_ippool: Could not find nas ip address in packet.");
300 return RLM_MODULE_NOOP;
304 /* We don't care about any other accounting packet */
306 return RLM_MODULE_NOOP;
311 key_datum.dptr = (ippool_key *) &key;
312 key_datum.dsize = sizeof(ippool_key);
314 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
315 data_datum = gdbm_fetch(data->gdbm, key_datum);
316 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
317 if (data_datum.dptr != NULL){
320 * If the entry was found set active to zero
322 memcpy(&entry, data_datum.dptr, sizeof(int));
323 free(data_datum.dptr);
324 DEBUG("rlm_ippool: Deallocated entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
327 data_datum.dptr = (ippool_info *) &entry;
328 data_datum.dsize = sizeof(ippool_info);
330 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
331 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
332 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
334 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
335 data->session_db, gdbm_strerror(gdbm_errno));
336 return RLM_MODULE_FAIL;
340 * Decrease allocated count from the ip index
342 key_datum.dptr = (uint32_t *) &entry.ipaddr;
343 key_datum.dsize = sizeof(uint32_t);
344 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
345 data_datum = gdbm_fetch(data->ip, key_datum);
346 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
347 if (data_datum.dptr != NULL){
348 memcpy(&num, data_datum.dptr, sizeof(int));
349 free(data_datum.dptr);
352 DEBUG("rlm_ippool: num: %d",num);
353 data_datum.dptr = (int *) #
354 data_datum.dsize = sizeof(int);
355 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
356 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
357 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
359 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
360 data->ip_index, gdbm_strerror(gdbm_errno));
361 return RLM_MODULE_FAIL;
367 return RLM_MODULE_OK;
370 static int ippool_authorize(void *instance, REQUEST *request)
372 rlm_ippool_t *data = (rlm_ippool_t *) instance;
388 /* quiet the compiler */
392 /* Check if Pool-Name attribute exists. If it exists check our name and
393 * run only if they match
395 if ((vp = pairfind(request->config_items, PW_POOL_NAME)) != NULL){
396 if (data->name == NULL || strcmp(data->name,vp->strvalue))
397 return RLM_MODULE_NOOP;
401 * Get the nas ip address
403 if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
406 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
413 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
419 if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL){
424 DEBUG("rlm_ippool: Searching for an entry for nas/port: %s/%d",ip_ntoa(str,nas),port);
425 key_datum.dptr = (ippool_key *) &key;
426 key_datum.dsize = sizeof(ippool_key);
428 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
429 data_datum = gdbm_fetch(data->gdbm, key_datum);
430 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
431 if (data_datum.dptr != NULL){
433 * If there is a corresponding entry in the database with active=1 it is stale.
436 memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
437 free(data_datum.dptr);
439 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
442 data_datum.dptr = (ippool_info *) &entry;
443 data_datum.dsize = sizeof(ippool_info);
445 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
446 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
447 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
449 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
450 data->session_db, gdbm_strerror(gdbm_errno));
451 return RLM_MODULE_FAIL;
453 /* Decrease allocated count from the ip index */
455 key_datum.dptr = (uint32_t *) &entry.ipaddr;
456 key_datum.dsize = sizeof(uint32_t);
457 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
458 data_datum = gdbm_fetch(data->ip, key_datum);
459 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
460 if (data_datum.dptr != NULL){
461 memcpy(&num, data_datum.dptr, sizeof(int));
462 free(data_datum.dptr);
465 DEBUG("rlm_ippool: num: %d",num);
466 data_datum.dptr = (int *) #
467 data_datum.dsize = sizeof(int);
468 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
469 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
470 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
472 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
473 data->ip_index, gdbm_strerror(gdbm_errno));
474 return RLM_MODULE_FAIL;
482 * If there is a Framed-IP-Address attribute in the reply do nothing
484 if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
485 return RLM_MODULE_NOOP;
488 * Walk through the database searching for an active=0 entry.
491 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
492 key_datum = gdbm_firstkey(data->gdbm);
493 while(key_datum.dptr){
494 data_datum = gdbm_fetch(data->gdbm, key_datum);
495 memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
496 free(data_datum.dptr);
498 * If we find an entry for the same caller-id and nas with active=0
499 * then we use that for multilink (MPPP) to work properly.
501 if (cli != NULL && strcmp(entry.cli,cli) == 0 && entry.active){
502 memcpy(&key,key_datum.dptr,sizeof(ippool_key));
506 if (entry.active == 0){
509 tmp.dptr = (uint32_t *) &entry.ipaddr;
510 tmp.dsize = sizeof(uint32_t);
511 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
512 data_datum = gdbm_fetch(data->ip, tmp);
513 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
516 * If we find an entry in the ip index and the number is zero (meaning
517 * that we haven't allocated the same ip address to another nas/port pair)
518 * or if we don't find an entry then delete the session entry so
519 * that we can change the key (nas/port)
520 * Else we don't delete the session entry since we haven't yet deallocated the
521 * corresponding ip address and we continue our search.
524 if (data_datum.dptr){
525 memcpy(&num,data_datum.dptr, sizeof(int));
526 free(data_datum.dptr);
537 nextkey = gdbm_nextkey(data->gdbm, key_datum);
538 free(key_datum.dptr);
541 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
543 * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
548 data_datum.dptr = (ippool_info *) &entry;
549 data_datum.dsize = sizeof(ippool_info);
553 * Delete the entry so that we can change the key
555 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
556 gdbm_delete(data->gdbm, key_datum);
557 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
559 free(key_datum.dptr);
562 key_datum.dptr = (ippool_key *) &key;
563 key_datum.dsize = sizeof(ippool_key);
565 if (data->fd >= 0) rad_lockfd(data->fd, sizeof(int));
566 rcode = gdbm_store(data->gdbm, key_datum, data_datum, GDBM_REPLACE);
567 if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
569 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
570 data->session_db, gdbm_strerror(gdbm_errno));
571 return RLM_MODULE_FAIL;
574 /* Increase the ip index count */
575 key_datum.dptr = (uint32_t *) &entry.ipaddr;
576 key_datum.dsize = sizeof(uint32_t);
577 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
578 data_datum = gdbm_fetch(data->ip, key_datum);
579 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
580 if (data_datum.dptr){
581 memcpy(&num,data_datum.dptr,sizeof(int));
582 free(data_datum.dptr);
585 DEBUG("rlm_ippool: num: %d",num);
586 data_datum.dptr = (int *) #
587 data_datum.dsize = sizeof(int);
588 if (data->ip_fd >= 0) rad_lockfd(data->ip_fd, sizeof(int));
589 rcode = gdbm_store(data->ip, key_datum, data_datum, GDBM_REPLACE);
590 if (data->ip_fd >= 0) rad_unlockfd(data->ip_fd, sizeof(int));
592 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
593 data->ip_index, gdbm_strerror(gdbm_errno));
594 return RLM_MODULE_FAIL;
598 DEBUG("rlm_ippool: Allocated ip %s to client on nas %s,port %d",ip_ntoa(str,entry.ipaddr),
599 ip_ntoa(str2,key.nas),port);
600 if ((vp = paircreate(PW_FRAMED_IP_ADDRESS, PW_TYPE_IPADDR)) == NULL) {
601 radlog(L_ERR|L_CONS, "no memory");
602 return RLM_MODULE_NOOP;
604 vp->lvalue = entry.ipaddr;
605 pairadd(&request->reply->vps, vp);
608 DEBUG("rlm_ippool: No available ip addresses in pool.");
609 return RLM_MODULE_NOOP;
612 return RLM_MODULE_OK;
615 static int ippool_detach(void *instance)
617 rlm_ippool_t *data = (rlm_ippool_t *) instance;
619 gdbm_close(data->gdbm);
620 gdbm_close(data->ip);
621 free(data->session_db);
622 free(data->ip_index);
629 * The module name should be the only globally exported symbol.
630 * That is, everything else should be 'static'.
632 * If the module needs to temporarily modify it's instantiation
633 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
634 * The server will then take care of ensuring that the module
635 * is single-threaded.
637 module_t rlm_ippool = {
639 RLM_TYPE_THREAD_SAFE, /* type */
640 NULL, /* initialization */
641 ippool_instantiate, /* instantiation */
643 NULL, /* authentication */
644 ippool_authorize, /* authorization */
645 NULL, /* preaccounting */
646 ippool_accounting, /* accounting */
647 NULL /* checksimul */
649 ippool_detach, /* detach */