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