Make counter and ippool modules thread safe since we now do file level locking
[freeradius.git] / src / modules / rlm_ippool / rlm_ippool.c
1 /*
2  * rlm_ippool.c
3  *
4  * Version:  $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  * Copyright 2002  Kostas Kalevras <kkalev@noc.ntua.gr>
22  */
23
24 #include "config.h"
25 #include "autoconf.h"
26 #include "libradius.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include "radiusd.h"
34 #include "modules.h"
35 #include "conffile.h"
36
37 #include <gdbm.h>
38 #include <time.h>
39 #include <netinet/in.h>
40
41 #ifdef NEEDS_GDBM_SYNC
42 #       define GDBM_SYNCOPT GDBM_SYNC
43 #else
44 #       define GDBM_SYNCOPT 0
45 #endif
46
47 #ifdef GDBM_NOLOCK
48 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT | GDBM_NOLOCK)
49 #else
50 #define GDBM_IPPOOL_OPTS (GDBM_SYNCOPT)
51 #endif
52
53 #ifndef HAVE_GDBM_FDESC
54 #define gdbm_fdesc(foo) (-1)
55 #endif
56
57 #define ALL_ONES 4294967295
58
59 static const char rcsid[] = "$Id$";
60
61 /*
62  *      Define a structure for our module configuration.
63  *
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.
67  */
68 typedef struct rlm_ippool_t {
69         char *session_db;
70         char *ip_index;
71         uint32_t range_start;
72         uint32_t range_stop;
73         uint32_t netmask;
74         int cache_size;
75         GDBM_FILE gdbm;
76         GDBM_FILE ip;
77         int fd;
78         int ip_fd;
79 } rlm_ippool_t;
80
81 typedef struct ippool_info {
82         uint32_t        ipaddr;
83         char            active;
84         char            cli[32];
85 } ippool_info;
86
87 typedef struct ippool_key {
88         uint32_t nas;
89         int port;
90 } ippool_key;
91
92 /*
93  *      A mapping of configuration file names to internal variables.
94  *
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
99  *      buffer over-flows.
100  */
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 }
109 };
110
111
112 /*
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.
117  *
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.
121  */
122 static int ippool_instantiate(CONF_SECTION *conf, void **instance)
123 {
124         rlm_ippool_t *data;
125         int cache_size;
126         ippool_info entry;
127         ippool_key key;
128         datum key_datum;
129         datum data_datum;
130         int i,j;
131         char *cli = "0";
132         
133         /*
134          *      Set up a storage area for instance data
135          */
136         data = rad_malloc(sizeof(*data));
137
138         /*
139          *      If the configuration parameters can't be parsed, then
140          *      fail.
141          */
142         if (cf_section_parse(conf, data, module_config) < 0) {
143                 free(data);
144                 return -1;
145         }
146         cache_size = data->cache_size;
147
148         if (data->session_db == NULL) {
149                 radlog(L_ERR, "rlm_ippool: 'session-db' must be set.");
150                 free(data);
151                 return -1;
152         }
153         if (data->ip_index == NULL) {
154                 radlog(L_ERR, "rlm_ippool: 'ip-index' must be set.");
155                 free(data);
156                 return -1;
157         }
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.");
164                 free(data);
165                 return -1;
166         }
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));
172                 return -1;
173         }
174         if (data->fd >= 0) data->fd = gdbm_fdesc(data->gdbm);
175
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));
181                 return -1;
182         }
183         if (data->ip_fd >= 0) data->ip_fd = gdbm_fdesc(data->ip);
184
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");
189
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){
194                         /*
195                          * If the database does not exist initialize it.
196                          * We set the nas/port pairs to not existent values and
197                          * active = 0
198                          */
199                 int rcode;
200                 uint32_t or_result;
201                 char str[32];
202
203                 DEBUG("rlm_ippool: Initializing database");
204                 for(i=data->range_start,j=-1;i<=data->range_stop;i++,j--){
205
206                         /*
207                          * Net and Broadcast addresses are excluded
208                          */
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)));
212                                 continue;
213                         }
214                         
215                         key.nas = 0;
216                         key.port = j;
217                         key_datum.dptr = (ippool_key *) &key;
218                         key_datum.dsize = sizeof(ippool_key);
219
220                         entry.ipaddr = ntohl(i);
221                         entry.active = 0;
222                         strcpy(entry.cli,cli);
223
224                         data_datum.dptr = (ippool_info *) &entry;
225                         data_datum.dsize = sizeof(ippool_info);
226
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));
230                         if (rcode < 0) {
231                                 radlog(L_ERR, "rlm_ippool: Failed storing data to %s: %s",
232                                                 data->session_db, gdbm_strerror(gdbm_errno));
233                                 free(data);
234                                 gdbm_close(data->gdbm);
235                                 gdbm_close(data->ip);
236                                 return -1;
237                         }
238                 }
239         }
240         else
241                 free(key_datum.dptr);
242
243         *instance = data;
244         
245         return 0;
246 }
247
248
249 /*
250  *      Check for an Accounting-Stop
251  *      If we find one and we have allocated an IP to this nas/port combination, deallocate it. 
252  */
253 static int ippool_accounting(void *instance, REQUEST *request)
254 {
255         rlm_ippool_t *data = (rlm_ippool_t *)instance;
256         datum key_datum;
257         datum data_datum;
258         int acctstatustype = 0;
259         int port = -1;
260         int rcode;
261         uint32_t nas = 0;
262         ippool_info entry;
263         ippool_key key;
264         int num = 0;
265         VALUE_PAIR *vp;
266         char str[32];
267
268
269         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL)
270                 acctstatustype = vp->lvalue;
271         else {
272                 DEBUG("rlm_ippool: Could not find account status type in packet.");
273                 return RLM_MODULE_NOOP;
274         }
275         switch(acctstatustype){
276                 case PW_STATUS_STOP:
277                         if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL)
278                                 port = vp->lvalue;
279                         else {
280                                 DEBUG("rlm_ippool: Could not find port number in packet.");
281                                 return RLM_MODULE_NOOP;
282                         }
283                         if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
284                                 nas = vp->lvalue;
285                         else {
286                                 DEBUG("rlm_ippool: Could not find nas ip address in packet.");
287                                 return RLM_MODULE_NOOP;
288                         }
289                         break;
290                 default:
291                         /* We don't care about any other accounting packet */
292
293                         return RLM_MODULE_NOOP;
294         }
295
296         key.nas = nas;
297         key.port = port;
298         key_datum.dptr = (ippool_key *) &key;
299         key_datum.dsize = sizeof(ippool_key);
300
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){
305
306                 /*
307                  * If the entry was found set active to zero
308                  */
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);
312                 entry.active = 0;
313
314                 data_datum.dptr = (ippool_info *) &entry;
315                 data_datum.dsize = sizeof(ippool_info);
316
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));
320                 if (rcode < 0) {
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;
324                 }
325
326                 /*
327                  * Decrease allocated count from the ip index
328                  */
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);
337                         if (num >0){
338                                 num--;
339                                 DEBUG("rlm_ippool: num: %d",num);
340                                 data_datum.dptr = (int *) &num;
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));
345                                 if (rcode < 0) {
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;
349                                 }
350                         }
351                 }
352         }
353
354         return RLM_MODULE_OK;
355 }
356
357 static int ippool_authorize(void *instance, REQUEST *request)
358 {
359         rlm_ippool_t *data = (rlm_ippool_t *) instance;
360         int port = 0;
361         int delete = 0;
362         int rcode;
363         int num = 0;
364         uint32_t nas = 0;
365         datum key_datum;
366         datum nextkey;
367         datum data_datum;
368         ippool_key key;
369         ippool_info entry;
370         VALUE_PAIR *vp;
371         char str[32];
372         char str2[32];
373         char *cli = NULL;
374
375         /* quiet the compiler */
376         instance = instance;
377         request = request;
378
379         /*
380          * Get the nas ip address
381          */
382         if ((vp = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
383                 nas = vp->lvalue;
384         else{
385                 if ((vp = pairfind(request->packet->vps, PW_NAS_IDENTIFIER)) != NULL)
386                         nas = vp->lvalue;
387         }
388
389         /*
390          * Find the caller id
391          */
392         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
393                 cli = vp->strvalue;
394
395         /*
396          * Find the port
397          */
398         if ((vp = pairfind(request->packet->vps, PW_NAS_PORT_ID)) != NULL){
399                 port = vp->lvalue;
400
401                 key.nas = nas;
402                 key.port = port;        
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);
406
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){
411                         /*
412                          * If there is a corresponding entry in the database with active=1 it is stale.
413                          * Set active to zero
414                          */
415                         memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
416                         free(data_datum.dptr);
417                         if (entry.active){
418                                 DEBUG("rlm_ippool: Found a stale entry for ip/port: %s/%d",ip_ntoa(str,entry.ipaddr),port);
419                                 entry.active = 0;
420
421                                 data_datum.dptr = (ippool_info *) &entry;
422                                 data_datum.dsize = sizeof(ippool_info);
423
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));
427                                 if (rcode < 0) {
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;
431                                 }
432                                 /* Decrease allocated count from the ip index */
433
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);
442                                         if (num >0){
443                                                 num--;
444                                                 DEBUG("rlm_ippool: num: %d",num);
445                                                 data_datum.dptr = (int *) &num;
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));
450                                                 if (rcode < 0) {
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;
454                                                 }
455                                         }
456                                 }
457                         }
458                 }
459         }
460         /*
461          * If there is a Framed-IP-Address attribute in the reply do nothing
462          */
463         if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS) != NULL)
464                 return RLM_MODULE_NOOP;
465
466         /*
467          * Walk through the database searching for an active=0 entry.
468          */
469
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));
478                         if (key.nas == nas)     
479                                 break;
480                 }
481                 if (entry.active == 0){
482                         datum tmp;              
483
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));
489
490                         /*
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.
497                          */
498
499                         if (data_datum.dptr){
500                                 memcpy(&num,data_datum.dptr, sizeof(int));
501                                 free(data_datum.dptr);
502                                 if (num == 0){
503                                         delete = 1;
504                                         break;
505                                 }
506                         }
507                         else{
508                                 delete = 1;
509                                 break;
510                         }
511                 }
512                 nextkey = gdbm_nextkey(data->gdbm, key_datum);
513                 free(key_datum.dptr);
514                 key_datum = nextkey;
515         }
516         if (data->fd >= 0) rad_unlockfd(data->fd, sizeof(int));
517         /*
518          * If we have found a free entry set active to 1 then add a Framed-IP-Address attribute to
519          * the reply
520          */
521         if (key_datum.dptr){
522                 entry.active = 1;
523                 data_datum.dptr = (ippool_info *) &entry;
524                 data_datum.dsize = sizeof(ippool_info);
525
526                 if (delete){
527                         /*
528                          * Delete the entry so that we can change the key
529                          */
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));
533                 }
534                 free(key_datum.dptr);
535                 key.nas = nas;
536                 key.port = port;
537                 key_datum.dptr = (ippool_key *) &key;
538                 key_datum.dsize = sizeof(ippool_key);
539                 
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));
543                 if (rcode < 0) {
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;
547                 }
548
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);
558                 }
559                 num++;
560                 DEBUG("rlm_ippool: num: %d",num);
561                 data_datum.dptr = (int *) &num;
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));
566                 if (rcode < 0) {
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;
570                 }
571                         
572
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;
578                 }
579                 vp->lvalue = entry.ipaddr;
580                 pairadd(&request->reply->vps, vp);
581         }
582         else{
583                 DEBUG("rlm_ippool: No available ip addresses in pool.");
584                 return RLM_MODULE_NOOP;
585         }
586
587         return RLM_MODULE_OK;
588 }
589
590 static int ippool_detach(void *instance)
591 {
592         rlm_ippool_t *data = (rlm_ippool_t *) instance;
593
594         gdbm_close(data->gdbm);
595         gdbm_close(data->ip);
596         free(data->session_db);
597         free(data->ip_index);
598
599         free(instance);
600         return 0;
601 }
602
603 /*
604  *      The module name should be the only globally exported symbol.
605  *      That is, everything else should be 'static'.
606  *
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.
611  */
612 module_t rlm_ippool = {
613         "IPPOOL",       
614         RLM_TYPE_THREAD_SAFE,           /* type */
615         NULL,                           /* initialization */
616         ippool_instantiate,             /* instantiation */
617         {
618                 NULL,                   /* authentication */
619                 ippool_authorize,       /* authorization */
620                 NULL,                   /* preaccounting */
621                 ippool_accounting,      /* accounting */
622                 NULL                    /* checksimul */
623         },
624         ippool_detach,                  /* detach */
625         NULL,                           /* destroy */
626 };