1 /* This file is part of GNU Radius.
2 Copyright (C) 2000,2001,2002,2003,2004,2005,
3 2006,2007,2008 Free Software Foundation, Inc.
4
5 Written by Sergey Poznyakoff
6
7 GNU Radius is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU Radius is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Radius; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/file.h>
28 #include <sys/stat.h>
29 #include <sys/mman.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <ctype.h>
37 #include <netinet/in.h>
38 #include <fcntl.h>
39
40 #include <radiusd.h>
41
42 #ifdef USE_SNMP
43
44 /* ************************************************************************* */
45 /* Shared memory interface */
46
47 static int statfile_perms = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
48 static int tempfd = -1;
49 static unsigned offset;
50 static char *shmem_base;
51 static unsigned shmem_size;
52
53 #ifndef MAP_FAILED
54 # define MAP_FAILED (void*)-1
55 #endif
56
57 int
shmem_alloc(size_t size)58 shmem_alloc(size_t size)
59 {
60 struct stat sb;
61 int init = 0;
62
63 if (tempfd == -1) {
64 tempfd = open(grad_stat_file, O_RDWR);
65 if (tempfd == -1) {
66 if (errno == ENOENT)
67 tempfd = open(grad_stat_file,
68 O_RDWR|O_CREAT|O_TRUNC,
69 statfile_perms);
70
71 if (tempfd == -1) {
72 grad_log(GRAD_LOG_ERR|GRAD_LOG_PERROR,
73 _("can't open file `%s'"),
74 grad_stat_file);
75 return -1;
76 }
77 }
78 if (fstat(tempfd, &sb)) {
79 grad_log(GRAD_LOG_ERR|GRAD_LOG_PERROR, _("can't stat `%s'"),
80 grad_stat_file);
81 close(tempfd);
82 return -1;
83 }
84 if ((sb.st_mode & statfile_perms) != statfile_perms) {
85 grad_log(GRAD_LOG_ERR,
86 _("SNMP system disabled: file `%s' has incorrect permissions"),
87 grad_stat_file);
88 close(tempfd);
89 return -1;
90 }
91
92 if (sb.st_size < size) {
93 int c = 0;
94 init = 1;
95 lseek(tempfd, size - 1, SEEK_SET);
96 write(tempfd, &c, 1);
97 } else if (sb.st_size > size)
98 init = 1;
99 }
100
101 shmem_base = mmap((caddr_t)0, size, PROT_READ|PROT_WRITE, MAP_SHARED,
102 tempfd, 0);
103
104 if (shmem_base == MAP_FAILED) {
105 grad_log(GRAD_LOG_ERR|GRAD_LOG_PERROR, _("mmap failed"));
106 return -1;
107 } else {
108 shmem_size = size;
109 if (init)
110 memset(shmem_base, 0, size);
111 }
112 return 0;
113 }
114
115 void
shmem_free()116 shmem_free()
117 {
118 munmap(shmem_base, shmem_size);
119 close(tempfd);
120 tempfd = -1;
121 offset = 0;
122 }
123
124 void *
shmem_get(size_t size,int zero)125 shmem_get(size_t size, int zero)
126 {
127 void *ptr = NULL;
128
129 if (!shmem_base && shmem_alloc(size))
130 return NULL;
131 if (shmem_size - offset < size) {
132 grad_log(GRAD_LOG_ERR,
133 ngettext("shmem_get(): can't allocate %d byte",
134 "shmem_get(): can't allocate %d bytes",
135 size),
136 size);
137 } else {
138 ptr = shmem_base + offset;
139 offset += size;
140 if (zero)
141 memset(ptr, 0, size);
142 }
143 return ptr;
144 }
145
146
147
148 /* ************************************************************************* */
149 /* Server and port statistics */
150
151 int stat_port_count = 1024;
152 int stat_nas_count = 512;
153
154 extern Server_stat *server_stat;
155 extern struct radstat radstat;
156 PORT_STAT *port_stat;
157 struct nas_stat *nas_stat;
158
159 void
stat_init()160 stat_init()
161 {
162 struct timeval tv;
163 struct timezone tz;
164 int init;
165
166 if (shmem_alloc(stat_port_count * sizeof(PORT_STAT) +
167 sizeof(*server_stat) +
168 stat_nas_count * sizeof(struct nas_stat))) {
169 grad_log(GRAD_LOG_NOTICE, _("stat_init failed"));
170 return;
171 }
172
173 server_stat = shmem_get(sizeof(*server_stat), 0);
174 init = server_stat->port_count != stat_port_count;
175 server_stat->port_count = stat_port_count;
176 server_stat->nas_count = stat_nas_count;
177
178 port_stat = shmem_get(stat_port_count * sizeof(PORT_STAT), init);
179
180 nas_stat = shmem_get(stat_nas_count * sizeof(struct nas_stat), 1);
181
182 gettimeofday(&tv, &tz);
183 server_stat->start_time = tv; /* FIXME? */
184 server_stat->auth.reset_time = tv;
185 server_stat->acct.reset_time = tv;
186 server_stat->auth.status = serv_running;
187 server_stat->acct.status = serv_running;
188 }
189
190 void
stat_done()191 stat_done()
192 {
193 if (server_stat) {
194 shmem_free();
195 server_stat = NULL;
196 }
197 }
198
199 #define FOR_EACH_PORT(p) \
200 for (p = port_stat; p < port_stat + stat_port_count; p++)
201
202 static PORT_STAT *
stat_alloc_port()203 stat_alloc_port()
204 {
205 PORT_STAT *port;
206
207 FOR_EACH_PORT(port) {
208 if (port->ip == 0)
209 return port;
210 }
211 return NULL;
212 }
213
214 PORT_STAT *
stat_find_port(grad_nas_t * nas,int port_no)215 stat_find_port(grad_nas_t *nas, int port_no)
216 {
217 PORT_STAT *port;
218
219 if (!server_stat)
220 return NULL;
221 FOR_EACH_PORT(port) {
222 if (port->ip == 0)
223 break;
224 if (grad_ip_in_net_p(&nas->netdef, port->ip)
225 && port->port_no == port_no)
226 return port;
227 }
228
229 /* Port not found */
230 if ((port = stat_alloc_port()) == NULL) {
231 grad_log(GRAD_LOG_WARN,
232 _("reached SNMP storage limit for the number of monitored ports: increase max-port-count"));
233 return NULL;
234 }
235 port->ip = nas->netdef.ipaddr;
236 port->port_no = port_no;
237
238 GRAD_DEBUG1(1, "next offset %d", port - port_stat);
239
240 return port;
241 }
242
243 int
stat_get_port_index(grad_nas_t * nas,int port_no)244 stat_get_port_index(grad_nas_t *nas, int port_no)
245 {
246 PORT_STAT *port;
247
248 if (!server_stat)
249 return 0;
250 FOR_EACH_PORT(port) {
251 if (port->ip == 0)
252 break;
253 if (grad_ip_in_net_p(&nas->netdef, port->ip)
254 && port->port_no == port_no)
255 return port - port_stat + 1;
256 }
257 return 0;
258 }
259
260 int
stat_get_next_port_no(grad_nas_t * nas,int port_no)261 stat_get_next_port_no(grad_nas_t *nas, int port_no)
262 {
263 PORT_STAT *port;
264 int next = stat_port_count;
265
266 if (!server_stat)
267 return 0;
268 FOR_EACH_PORT(port) {
269 if (port->ip == 0)
270 break;
271 if (grad_ip_in_net_p(&nas->netdef, port->ip)
272 && port->port_no > port_no
273 && port->port_no < next)
274 next = port->port_no;
275 }
276 return (next == stat_port_count) ? 0 : next;
277 }
278
279 void
stat_update(struct radutmp * ut,int status)280 stat_update(struct radutmp *ut, int status)
281 {
282 grad_nas_t *nas;
283 PORT_STAT *port;
284 long dt;
285 char ipbuf[GRAD_IPV4_STRING_LENGTH];
286
287 if (!server_stat)
288 return;
289 nas = grad_nas_lookup_ip(ntohl(ut->nas_address));
290 if (!nas) {
291 grad_log(GRAD_LOG_WARN,
292 _("stat_update(): portno %d: can't find nas for IP %s"),
293 ut->nas_port,
294 grad_ip_iptostr(ntohl(ut->nas_address), ipbuf));
295 return;
296 }
297 if (nas->netdef.ipaddr == 0) /* DEFAULT nas */
298 return;
299
300 port = stat_find_port(nas, ut->nas_port);
301 if (!port) {
302 grad_log(GRAD_LOG_WARN,
303 _("stat_update(): port %d not found on NAS %s"),
304 ut->nas_port,
305 grad_ip_iptostr(ntohl(ut->nas_address), ipbuf));
306 return;
307 }
308
309 switch (status) {
310 case DV_ACCT_STATUS_TYPE_START:
311 if (port->start) {
312 dt = ut->time - port->start;
313 if (dt < 0) {
314 grad_log(GRAD_LOG_NOTICE,
315 "stat_update(START,%s,%s,%d): %s",
316 ut->login, nas->shortname, ut->nas_port,
317 _("negative port idle time"));
318 } else {
319 port->idle += dt;
320 }
321 if (dt > port->maxidle.time) {
322 port->maxidle.time = dt;
323 port->maxidle.start = port->start;
324 }
325 }
326
327 strncpy(port->login, ut->login, sizeof(port->login));
328 port->framed_address = ut->framed_address;
329 port->active = 1;
330 port->count++;
331 port->start = port->lastin = ut->time;
332 break;
333
334 case DV_ACCT_STATUS_TYPE_STOP:
335 if (port->start) {
336 dt = ut->time - port->start;
337 if (dt < 0) {
338 grad_log(GRAD_LOG_NOTICE,
339 "stat_update(STOP,%s,%s,%d): %s",
340 ut->login, nas->shortname, ut->nas_port,
341 _("negative port session time"));
342 } else {
343 port->inuse += dt;
344 }
345 if (dt > port->maxinuse.time) {
346 port->maxinuse.time = dt;
347 port->maxinuse.start = port->start;
348 }
349 }
350
351 port->active = 0;
352 port->start = port->lastout = ut->time;
353 break;
354
355 case DV_ACCT_STATUS_TYPE_ALIVE:
356 strncpy(port->login, ut->login, sizeof(port->login));
357 port->framed_address = ut->framed_address;
358 port->active = 1;
359 break;
360 }
361
362 GRAD_DEBUG9(1,
363 "NAS %#x port %d: act %d, cnt %d, start %d, inuse %d/%d idle %d/%d",
364 port->ip, port->port_no, port->active,
365 port->count, port->start,
366 port->inuse, port->maxinuse.time,
367 port->idle, port->maxidle.time);
368 }
369
370 void
stat_count_ports()371 stat_count_ports()
372 {
373 grad_nas_t *nas;
374 struct nas_stat *statp;
375 PORT_STAT *port;
376 grad_iterator_t *itr;
377
378 if (!server_stat)
379 return;
380 itr = grad_nas_iterator();
381 for (nas = grad_iterator_first(itr); nas; nas = grad_iterator_next(itr)) {
382 statp = nas->app_data;
383 statp->ports_active = statp->ports_idle = 0;
384 }
385 grad_iterator_destroy(&itr);
386
387 radstat.port_active_count = radstat.port_idle_count = 0;
388
389 FOR_EACH_PORT(port) {
390 if (port->ip == 0)
391 break;
392
393 nas = grad_nas_lookup_ip(port->ip);
394 if (!nas) {
395 /* Silently ignore */
396 continue;
397 }
398 statp = nas->app_data;
399 if (port->active) {
400 statp->ports_active++;
401 radstat.port_active_count++;
402 } else {
403 statp->ports_idle++;
404 radstat.port_idle_count++;
405 }
406 }
407 }
408
409 PORT_STAT *
findportbyindex(int ind)410 findportbyindex(int ind)
411 {
412 PORT_STAT *p;
413 int i;
414
415 if (!server_stat
416 || ind < 1
417 || ind > stat_port_count)
418 return NULL;
419 i = 1;
420 FOR_EACH_PORT(p) {
421 if (i == ind || p->ip == 0)
422 break;
423 i++;
424 }
425 if (p->ip == 0)
426 return NULL;
427 return p;
428 }
429
430
431 /* ************************************************************************* */
432 /* NAS statistics */
433
434 #define FOR_EACH_NAS(n) \
435 for (n = nas_stat; n < nas_stat + stat_nas_count; n++)
436
437 void
snmp_init_nas_stat()438 snmp_init_nas_stat()
439 {
440 if (!server_stat)
441 return;
442 server_stat->nas_index = 0;
443 }
444
445 /* For a given ip_address return NAS statistics info associated with it.
446 if no NAS with this address is known, return NULL */
447 struct nas_stat *
find_nas_stat(grad_uint32_t ip_addr)448 find_nas_stat(grad_uint32_t ip_addr)
449 {
450 struct nas_stat *np;
451
452 if (!server_stat)
453 return NULL;
454 FOR_EACH_NAS(np) {
455 if (np->ipaddr == ip_addr)
456 return np;
457 }
458 return NULL;
459 }
460
461 /* Attach NAS stat info to a given NAS structure. */
462 void
snmp_attach_nas_stat(grad_nas_t * nas)463 snmp_attach_nas_stat(grad_nas_t *nas)
464 {
465 struct nas_stat *np;
466
467 if (!server_stat)
468 return;
469 np = find_nas_stat(nas->netdef.ipaddr);
470 if (!np) {
471 if (server_stat->nas_index >= server_stat->nas_count) {
472 grad_log(GRAD_LOG_WARN,
473 _("reached SNMP storage limit for the number of monitored NASes: increase max-nas-count"));
474 return;
475 }
476 np = nas_stat + server_stat->nas_index;
477 ++server_stat->nas_index;
478 np->ipaddr = nas->netdef.ipaddr;
479 }
480 nas->app_data = np;
481 }
482
483 static int
nas_ip_cmp(const void * ap,const void * bp)484 nas_ip_cmp(const void *ap, const void *bp)
485 {
486 const struct nas_stat *a = ap, *b = bp;
487 return a->ipaddr - b->ipaddr;
488 }
489
490 void
snmp_sort_nas_stat()491 snmp_sort_nas_stat()
492 {
493 struct nas_stat *np;
494 int i;
495
496 if (!server_stat)
497 return;
498 qsort(nas_stat, server_stat->nas_index + 1,
499 sizeof(struct nas_stat*), nas_ip_cmp);
500 i = 1;
501 FOR_EACH_NAS(np) {
502 np->index = i++;
503 }
504 }
505
506
507 /* ************************************************************************* */
508 /* Configuration */
509 static int
stat_cfg_file(int argc,cfg_value_t * argv,void * block_data ARG_UNUSED,void * handler_data ARG_UNUSED)510 stat_cfg_file(int argc, cfg_value_t *argv,
511 void *block_data ARG_UNUSED, void *handler_data ARG_UNUSED)
512 {
513 if (argc != 2) {
514 cfg_argc_error(argc < 2);
515 return 0;
516 }
517
518 if (argv[1].type != CFG_STRING) {
519 cfg_type_error(CFG_STRING);
520 return 0;
521 }
522
523 grad_free(grad_stat_file);
524 if (argv[1].v.string[0] != '/')
525 grad_stat_file = grad_estrdup(argv[1].v.string);
526 else
527 grad_stat_file = grad_mkfilename(grad_log_dir, argv[1].v.string);
528
529 return 0;
530 }
531
532 struct cfg_stmt storage_stmt[] = {
533 { "file", CS_STMT, NULL, stat_cfg_file, NULL, NULL, NULL },
534 { "perms", CS_STMT, NULL, cfg_get_integer, &statfile_perms, NULL, NULL },
535 { "max-port-count", CS_STMT, NULL,
536 cfg_get_integer, &stat_port_count, NULL, NULL },
537 { "max-nas-count", CS_STMT, NULL,
538 cfg_get_integer, &stat_nas_count, NULL, NULL },
539 { NULL, }
540 };
541
542 #endif
543