1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: e0d2b650f184c29588d371addb7f9e355502374c $
19  * @file radsniff.c
20  * @brief Capture, filter, and generate statistics for RADIUS traffic
21  *
22  * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23  * @copyright 2006 The FreeRADIUS server project
24  * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
25  */
26 
27 RCSID("$Id: e0d2b650f184c29588d371addb7f9e355502374c $")
28 
29 #define _LIBRADIUS 1
30 #include <time.h>
31 #include <math.h>
32 #include <freeradius-devel/libradius.h>
33 #include <freeradius-devel/event.h>
34 
35 #include <freeradius-devel/radpaths.h>
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/pcap.h>
38 #include <freeradius-devel/radsniff.h>
39 
40 #ifdef HAVE_COLLECTDC_H
41 #  include <collectd/client.h>
42 #endif
43 
44 #define RS_ASSERT(_x) if (!(_x) && !fr_assert(_x)) exit(1)
45 
46 static rs_t *conf;
47 static struct timeval start_pcap = {0, 0};
48 static char timestr[50];
49 
50 static rbtree_t *request_tree = NULL;
51 static rbtree_t *link_tree = NULL;
52 static fr_event_list_t *events;
53 static bool cleanup;
54 
55 static int self_pipe[2] = {-1, -1};		//!< Signals from sig handlers
56 
57 typedef int (*rbcmp)(void const *, void const *);
58 
59 static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
60 #ifdef RADIUSD_VERSION_COMMIT
61 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
62 #endif
63 #ifndef ENABLE_REPRODUCIBLE_BUILDS
64 ", built on " __DATE__ " at " __TIME__
65 #endif
66 ;
67 
68 static int rs_useful_codes[] = {
69 	PW_CODE_ACCESS_REQUEST,			//!< RFC2865 - Authentication request
70 	PW_CODE_ACCESS_ACCEPT,			//!< RFC2865 - Access-Accept
71 	PW_CODE_ACCESS_REJECT,			//!< RFC2865 - Access-Reject
72 	PW_CODE_ACCOUNTING_REQUEST,		//!< RFC2866 - Accounting-Request
73 	PW_CODE_ACCOUNTING_RESPONSE,		//!< RFC2866 - Accounting-Response
74 	PW_CODE_ACCESS_CHALLENGE,		//!< RFC2865 - Access-Challenge
75 	PW_CODE_STATUS_SERVER,			//!< RFC2865/RFC5997 - Status Server (request)
76 	PW_CODE_STATUS_CLIENT,			//!< RFC2865/RFC5997 - Status Server (response)
77 	PW_CODE_DISCONNECT_REQUEST,		//!< RFC3575/RFC5176 - Disconnect-Request
78 	PW_CODE_DISCONNECT_ACK,			//!< RFC3575/RFC5176 - Disconnect-Ack (positive)
79 	PW_CODE_DISCONNECT_NAK,			//!< RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
80 	PW_CODE_COA_REQUEST,			//!< RFC3575/RFC5176 - CoA-Request
81 	PW_CODE_COA_ACK,			//!< RFC3575/RFC5176 - CoA-Ack (positive)
82 	PW_CODE_COA_NAK,			//!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
83 };
84 
85 static const FR_NAME_NUMBER rs_events[] = {
86 	{ "received",	RS_NORMAL	},
87 	{ "norsp",	RS_LOST		},
88 	{ "rtx",	RS_RTX		},
89 	{ "noreq",	RS_UNLINKED	},
90 	{ "reused",	RS_REUSED	},
91 	{ "error",	RS_ERROR	},
92 	{  NULL , -1 }
93 };
94 
95 static void NEVER_RETURNS usage(int status);
96 
97 /** Fork and kill the parent process, writing out our PID
98  *
99  * @param pidfile the PID file to write our PID to
100  */
rs_daemonize(char const * pidfile)101 static void rs_daemonize(char const *pidfile)
102 {
103 	FILE *fp;
104 	pid_t pid, sid;
105 
106 	pid = fork();
107 	if (pid < 0) {
108 		exit(EXIT_FAILURE);
109 	}
110 
111 	/*
112 	 *	Kill the parent...
113 	 */
114 	if (pid > 0) {
115 		close(self_pipe[0]);
116 		close(self_pipe[1]);
117 		exit(EXIT_SUCCESS);
118 	}
119 
120 	/*
121 	 *	Continue as the child.
122 	 */
123 
124 	/* Create a new SID for the child process */
125 	sid = setsid();
126 	if (sid < 0) {
127 		exit(EXIT_FAILURE);
128 	}
129 
130 	/*
131 	 *	Change the current working directory. This prevents the current
132 	 *	directory from being locked; hence not being able to remove it.
133 	 */
134 	if ((chdir("/")) < 0) {
135 		exit(EXIT_FAILURE);
136 	}
137 
138 	/*
139 	 *	And write it AFTER we've forked, so that we write the
140 	 *	correct PID.
141 	 */
142 	fp = fopen(pidfile, "w");
143 	if (fp != NULL) {
144 		fprintf(fp, "%d\n", (int) sid);
145 		fclose(fp);
146 	} else {
147 		ERROR("Failed creating PID file %s: %s", pidfile, fr_syserror(errno));
148 		exit(EXIT_FAILURE);
149 	}
150 
151 	/*
152 	 *	Close stdout and stderr if they've not been redirected.
153 	 */
154 	if (isatty(fileno(stdout))) {
155 		if (!freopen("/dev/null", "w", stdout)) {
156 			exit(EXIT_FAILURE);
157 		}
158 	}
159 
160 	if (isatty(fileno(stderr))) {
161 		if (!freopen("/dev/null", "w", stderr)) {
162 			exit(EXIT_FAILURE);
163 		}
164 	}
165 }
166 
167 #define USEC 1000000
rs_tv_sub(struct timeval const * end,struct timeval const * start,struct timeval * elapsed)168 static void rs_tv_sub(struct timeval const *end, struct timeval const *start, struct timeval *elapsed)
169 {
170 	elapsed->tv_sec = end->tv_sec - start->tv_sec;
171 	if (elapsed->tv_sec > 0) {
172 		elapsed->tv_sec--;
173 		elapsed->tv_usec = USEC;
174 	} else {
175 		elapsed->tv_usec = 0;
176 	}
177 	elapsed->tv_usec += end->tv_usec;
178 	elapsed->tv_usec -= start->tv_usec;
179 
180 	if (elapsed->tv_usec >= USEC) {
181 		elapsed->tv_usec -= USEC;
182 		elapsed->tv_sec++;
183 	}
184 }
185 
rs_tv_add_ms(struct timeval const * start,unsigned long interval,struct timeval * result)186 static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result) {
187     result->tv_sec = start->tv_sec + (interval / 1000);
188     result->tv_usec = start->tv_usec + ((interval % 1000) * 1000);
189 
190     if (result->tv_usec > USEC) {
191 	result->tv_usec -= USEC;
192 	result->tv_sec++;
193     }
194 }
195 
rs_time_print(char * out,size_t len,struct timeval const * t)196 static void rs_time_print(char *out, size_t len, struct timeval const *t)
197 {
198 	size_t ret;
199 	struct timeval now;
200 	uint32_t usec;
201 
202 	if (!t) {
203 		gettimeofday(&now, NULL);
204 		t = &now;
205 	}
206 
207 	ret = strftime(out, len, "%Y-%m-%d %H:%M:%S", localtime(&t->tv_sec));
208 	if (ret >= len) {
209 		return;
210 	}
211 
212 	usec = t->tv_usec;
213 
214 	if (usec) {
215 		while (usec < 100000) usec *= 10;
216 		snprintf(out + ret, len - ret, ".%i", usec);
217 	} else {
218 		snprintf(out + ret, len - ret, ".000000");
219 	}
220 }
221 
rs_prints_csv(char * out,size_t outlen,char const * in,size_t inlen)222 static size_t rs_prints_csv(char *out, size_t outlen, char const *in, size_t inlen)
223 {
224 	char const	*start = out;
225 	uint8_t const	*str = (uint8_t const *) in;
226 
227 	if (!in) {
228 		if (outlen) {
229 			*out = '\0';
230 		}
231 
232 		return 0;
233 	}
234 
235 	if (inlen == 0) {
236 		inlen = strlen(in);
237 	}
238 
239 	while ((inlen > 0) && (outlen > 2)) {
240 		/*
241 		 *	Escape double quotes with... MORE DOUBLE QUOTES!
242 		 */
243 		if (*str == '"') {
244 			*out++ = '"';
245 			outlen--;
246 		}
247 
248 		/*
249 		 *	Safe chars which require no escaping
250 		 */
251 		if ((*str == '\r') || (*str == '\n') || ((*str >= '\x20') && (*str <= '\x7E'))) {
252 			*out++ = *str++;
253 			outlen--;
254 			inlen--;
255 
256 			continue;
257 		}
258 
259 		/*
260 		 *	Everything else is dropped
261 		 */
262 		str++;
263 		inlen--;
264 	}
265 	*out = '\0';
266 
267 	return out - start;
268 }
269 
rs_packet_print_csv_header(void)270 static void rs_packet_print_csv_header(void)
271 {
272 	char buffer[2048];
273 	char *p = buffer;
274 	int i;
275 
276 	ssize_t len, s = sizeof(buffer);
277 
278 	len = strlcpy(p, "\"Status\",\"Count\",\"Time\",\"Latency\",\"Type\",\"Interface\","
279 		      "\"Src IP\",\"Src Port\",\"Dst IP\",\"Dst Port\",\"ID\",", s);
280 	p += len;
281 	s -= len;
282 
283 	if (s <= 0) return;
284 
285 	for (i = 0; i < conf->list_da_num; i++) {
286 		char const *in;
287 
288 		*p++ = '"';
289 		s -= 1;
290 		if (s <= 0) return;
291 
292 		for (in = conf->list_da[i]->name; *in; in++) {
293 			*p++ = *in;
294 			s -= len;
295 			if (s <= 0) return;
296 		}
297 
298 		*p++ = '"';
299 		s -= 1;
300 		if (s <= 0) return;
301 		*p++ = ',';
302 		s -= 1;
303 		if (s <= 0) return;
304 	}
305 
306 	*--p = '\0';
307 
308 	fprintf(stdout , "%s\n", buffer);
309 }
310 
rs_packet_print_csv(uint64_t count,rs_status_t status,fr_pcap_t * handle,RADIUS_PACKET * packet,UNUSED struct timeval * elapsed,struct timeval * latency,UNUSED bool response,bool body)311 static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
312 				UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response,
313 				bool body)
314 {
315 	char const *status_str;
316 	char buffer[2048];
317 	char *p = buffer;
318 
319 	char src[INET6_ADDRSTRLEN];
320 	char dst[INET6_ADDRSTRLEN];
321 
322 	ssize_t len, s = sizeof(buffer);
323 
324 	inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
325 	inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
326 
327 	status_str = fr_int2str(rs_events, status, NULL);
328 	RS_ASSERT(status_str);
329 
330 	len = snprintf(p, s, "%s,%" PRIu64 ",%s,", status_str, count, timestr);
331 	p += len;
332 	s -= len;
333 
334 	if (s <= 0) return;
335 
336 	if (latency) {
337 		len = snprintf(p, s, "%u.%03u,",
338 			       (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
339 		p += len;
340 		s -= len;
341 	} else {
342 		*p = ',';
343 		p += 1;
344 		s -= 1;
345 	}
346 
347 	if (s <= 0) return;
348 
349 	/* Status, Type, Interface, Src, Src port, Dst, Dst port, ID */
350 	if (is_radius_code(packet->code)) {
351 		len = snprintf(p, s, "%s,%s,%s,%i,%s,%i,%i,", fr_packet_codes[packet->code], handle->name,
352 			       src, packet->src_port, dst, packet->dst_port, packet->id);
353 	} else {
354 		len = snprintf(p, s, "%u,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
355 			       src, packet->src_port, dst, packet->dst_port, packet->id);
356 	}
357 	p += len;
358 	s -= len;
359 
360 	if (s <= 0) return;
361 
362 	if (body) {
363 		int i;
364 		VALUE_PAIR *vp;
365 
366 		for (i = 0; i < conf->list_da_num; i++) {
367 			vp = fr_pair_find_by_da(packet->vps, conf->list_da[i], TAG_ANY);
368 			if (vp && (vp->vp_length > 0)) {
369 				if (conf->list_da[i]->type == PW_TYPE_STRING) {
370 					*p++ = '"';
371 					s--;
372 					if (s <= 0) return;
373 
374 					len = rs_prints_csv(p, s, vp->vp_strvalue, vp->vp_length);
375 					p += len;
376 					s -= len;
377 					if (s <= 0) return;
378 
379 					*p++ = '"';
380 					s--;
381 					if (s <= 0) return;
382 				} else {
383 					len = vp_prints_value(p, s, vp, 0);
384 					p += len;
385 					s -= len;
386 					if (s <= 0) return;
387 				}
388 			}
389 
390 			*p++ = ',';
391 			s -= 1;
392 			if (s <= 0) return;
393 		}
394 	} else {
395 		s -= conf->list_da_num;
396 		if (s <= 0) return;
397 
398 		memset(p, ',', conf->list_da_num);
399 		p += conf->list_da_num;
400 	}
401 
402 	*--p = '\0';
403 	fprintf(stdout , "%s\n", buffer);
404 }
405 
rs_packet_print_fancy(uint64_t count,rs_status_t status,fr_pcap_t * handle,RADIUS_PACKET * packet,struct timeval * elapsed,struct timeval * latency,bool response,bool body)406 static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
407 				  struct timeval *elapsed, struct timeval *latency, bool response, bool body)
408 {
409 	char buffer[2048];
410 	char *p = buffer;
411 
412 	char src[INET6_ADDRSTRLEN];
413 	char dst[INET6_ADDRSTRLEN];
414 
415 	ssize_t len, s = sizeof(buffer);
416 
417 	inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
418 	inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
419 
420 	/* Only print out status str if something's not right */
421 	if (status != RS_NORMAL) {
422 		char const *status_str;
423 
424 		status_str = fr_int2str(rs_events, status, NULL);
425 		RS_ASSERT(status_str);
426 
427 		len = snprintf(p, s, "** %s ** ", status_str);
428 		p += len;
429 		s -= len;
430 		if (s <= 0) return;
431 	}
432 
433 	if (is_radius_code(packet->code)) {
434 		len = snprintf(p, s, "%s Id %i %s:%s:%d %s %s:%i ",
435 			       fr_packet_codes[packet->code],
436 			       packet->id,
437 			       handle->name,
438 			       response ? dst : src,
439 			       response ? packet->dst_port : packet->src_port,
440 			       response ? "<-" : "->",
441 			       response ? src : dst ,
442 			       response ? packet->src_port : packet->dst_port);
443 	} else {
444 		len = snprintf(p, s, "%u Id %i %s:%s:%i %s %s:%i ",
445 			       packet->code,
446 			       packet->id,
447 			       handle->name,
448 			       response ? dst : src,
449 			       response ? packet->dst_port : packet->src_port,
450 			       response ? "<-" : "->",
451 			       response ? src : dst ,
452 			       response ? packet->src_port : packet->dst_port);
453 	}
454 	p += len;
455 	s -= len;
456 	if (s <= 0) return;
457 
458 	if (elapsed) {
459 		len = snprintf(p, s, "+%u.%03u ",
460 			       (unsigned int) elapsed->tv_sec, ((unsigned int) elapsed->tv_usec / 1000));
461 		p += len;
462 		s -= len;
463 		if (s <= 0) return;
464 	}
465 
466 	if (latency) {
467 		len = snprintf(p, s, "+%u.%03u ",
468 			       (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
469 		p += len;
470 		s -= len;
471 		if (s <= 0) return;
472 	}
473 
474 	*--p = '\0';
475 
476 	RIDEBUG("%s", buffer);
477 
478 	if (body) {
479 		/*
480 		 *	Print out verbose HEX output
481 		 */
482 		if (conf->print_packet && (fr_debug_lvl > 3)) {
483 			rad_print_hex(packet);
484 		}
485 
486 		if (conf->print_packet && (fr_debug_lvl > 1)) {
487 			char vector[(AUTH_VECTOR_LEN * 2) + 1];
488 
489 			if (packet->vps) {
490 				fr_pair_list_sort(&packet->vps, fr_pair_cmp_by_da_tag);
491 				vp_printlist(fr_log_fp, packet->vps);
492 			}
493 
494 			fr_bin2hex(vector, packet->vector, AUTH_VECTOR_LEN);
495 			INFO("\tAuthenticator-Field = 0x%s", vector);
496 		}
497 	}
498 }
499 
rs_packet_print(rs_request_t * request,uint64_t count,rs_status_t status,fr_pcap_t * handle,RADIUS_PACKET * packet,struct timeval * elapsed,struct timeval * latency,bool response,bool body)500 static inline void rs_packet_print(rs_request_t *request, uint64_t count, rs_status_t status, fr_pcap_t *handle,
501 				   RADIUS_PACKET *packet, struct timeval *elapsed, struct timeval *latency,
502 				   bool response, bool body)
503 {
504 	if (!conf->logger) return;
505 
506 	if (request) request->logged = true;
507 	conf->logger(count, status, handle, packet, elapsed, latency, response, body);
508 }
509 
rs_stats_print(rs_latency_t * stats,PW_CODE code)510 static void rs_stats_print(rs_latency_t *stats, PW_CODE code)
511 {
512 	int i;
513 	bool have_rt = false;
514 
515 	for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
516 		if (stats->interval.rt[i]) {
517 			have_rt = true;
518 		}
519 	}
520 
521 	if (!stats->interval.received && !have_rt && !stats->interval.reused) {
522 		return;
523 	}
524 
525 	if (stats->interval.received || stats->interval.linked) {
526 		INFO("%s counters:", fr_packet_codes[code]);
527 		if (stats->interval.received > 0) {
528 			INFO("\tTotal     : %.3lf/s" , stats->interval.received);
529 		}
530 	}
531 
532 	if (stats->interval.linked > 0) {
533 		INFO("\tLinked    : %.3lf/s", stats->interval.linked);
534 		INFO("\tUnlinked  : %.3lf/s", stats->interval.unlinked);
535 		INFO("%s latency:", fr_packet_codes[code]);
536 		INFO("\tHigh      : %.3lfms", stats->interval.latency_high);
537 		INFO("\tLow       : %.3lfms", stats->interval.latency_low);
538 		INFO("\tAverage   : %.3lfms", stats->interval.latency_average);
539 		INFO("\tMA        : %.3lfms", stats->latency_smoothed);
540 	}
541 
542 	if (have_rt || stats->interval.lost || stats->interval.reused) {
543 		INFO("%s retransmits & loss:",  fr_packet_codes[code]);
544 
545 		if (stats->interval.lost) {
546 			INFO("\tLost      : %.3lf/s", stats->interval.lost);
547 		}
548 
549 		if (stats->interval.reused) {
550 			INFO("\tID Reused : %.3lf/s", stats->interval.reused);
551 		}
552 
553 		for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
554 			if (!stats->interval.rt[i]) {
555 				continue;
556 			}
557 
558 			if (i != RS_RETRANSMIT_MAX) {
559 				INFO("\tRT (%i)    : %.3lf/s", i, stats->interval.rt[i]);
560 			} else {
561 				INFO("\tRT (%i+)   : %.3lf/s", i, stats->interval.rt[i]);
562 			}
563 		}
564 	}
565 }
566 
567 /** Query libpcap to see if it dropped any packets
568  *
569  * We need to check to see if libpcap dropped any packets and if it did, we need to stop stats output for long
570  * enough for inaccurate statistics to be cleared out.
571  *
572  * @param in pcap handle to check.
573  * @param interval time between checks (used for debug output)
574  * @return 0, no drops, -1 we couldn't check, -2 dropped because of buffer exhaustion, -3 dropped because of NIC.
575  */
rs_check_pcap_drop(fr_pcap_t * in,int interval)576 static int rs_check_pcap_drop(fr_pcap_t *in, int interval) {
577 	int ret = 0;
578 	struct pcap_stat pstats;
579 
580 	if (pcap_stats(in->handle, &pstats) != 0) {
581 		ERROR("%s failed retrieving pcap stats: %s", in->name, pcap_geterr(in->handle));
582 		return -1;
583 	}
584 
585 	INFO("\t%s%*s: %.3lf/s", in->name, (int) (10 - strlen(in->name)), "",
586 	     ((double) (pstats.ps_recv - in->pstats.ps_recv)) / interval);
587 
588 	if (pstats.ps_drop - in->pstats.ps_drop > 0) {
589 		ERROR("%s dropped %i packets: Buffer exhaustion", in->name, pstats.ps_drop - in->pstats.ps_drop);
590 		ret = -2;
591 	}
592 
593 	if (pstats.ps_ifdrop - in->pstats.ps_ifdrop > 0) {
594 		ERROR("%s dropped %i packets: Interface", in->name, pstats.ps_ifdrop - in->pstats.ps_ifdrop);
595 		ret = -3;
596 	}
597 
598 	in->pstats = pstats;
599 
600 	return ret;
601 }
602 
603 /** Update smoothed average
604  *
605  */
rs_stats_process_latency(rs_latency_t * stats)606 static void rs_stats_process_latency(rs_latency_t *stats)
607 {
608 	/*
609 	 *	If we didn't link any packets during this interval, we don't have a value to return.
610 	 *	returning 0 is misleading as it would be like saying the latency had dropped to 0.
611 	 *	We instead set NaN which libcollectd converts to a 'U' or unknown value.
612 	 *
613 	 *	This will cause gaps in graphs, but is completely legitimate as we are missing data.
614 	 *	This is unfortunately an effect of being just a passive observer.
615 	 */
616 	if (stats->interval.linked_total == 0) {
617 		double unk = strtod("NAN()", (char **) NULL);
618 
619 		stats->interval.latency_average = unk;
620 		stats->interval.latency_high = unk;
621 		stats->interval.latency_low = unk;
622 
623 		/*
624 		 *	We've not yet been able to determine latency, so latency_smoothed is also NaN
625 		 */
626 		if (stats->latency_smoothed_count == 0) {
627 			stats->latency_smoothed = unk;
628 		}
629 		return;
630 	}
631 
632 	if (stats->interval.linked_total && stats->interval.latency_total) {
633 		stats->interval.latency_average = (stats->interval.latency_total / stats->interval.linked_total);
634 	}
635 
636 	if (isnan(stats->latency_smoothed)) {
637 		stats->latency_smoothed = 0;
638 	}
639 	if (stats->interval.latency_average > 0) {
640 		stats->latency_smoothed_count++;
641 		stats->latency_smoothed += ((stats->interval.latency_average - stats->latency_smoothed) /
642 				       ((stats->latency_smoothed_count < 100) ? stats->latency_smoothed_count : 100));
643 	}
644 }
645 
rs_stats_process_counters(rs_latency_t * stats)646 static void rs_stats_process_counters(rs_latency_t *stats)
647 {
648 	int i;
649 
650 	stats->interval.received = ((long double) stats->interval.received_total) / conf->stats.interval;
651 	stats->interval.linked = ((long double) stats->interval.linked_total) / conf->stats.interval;
652 	stats->interval.unlinked = ((long double) stats->interval.unlinked_total) / conf->stats.interval;
653 	stats->interval.reused = ((long double) stats->interval.reused_total) / conf->stats.interval;
654 	stats->interval.lost = ((long double) stats->interval.lost_total) / conf->stats.interval;
655 
656 	for (i = 0; i < RS_RETRANSMIT_MAX; i++) {
657 		stats->interval.rt[i] = ((long double) stats->interval.rt_total[i]) / conf->stats.interval;
658 	}
659 }
660 
661 /** Process stats for a single interval
662  *
663  */
rs_stats_process(void * ctx)664 static void rs_stats_process(void *ctx)
665 {
666 	size_t i;
667 	size_t rs_codes_len = (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes));
668 	fr_pcap_t		*in_p;
669 	rs_update_t		*this = ctx;
670 	rs_stats_t		*stats = this->stats;
671 	struct timeval		now;
672 
673 	gettimeofday(&now, NULL);
674 
675 	stats->intervals++;
676 
677 	INFO("######### Stats Iteration %i #########", stats->intervals);
678 
679 	/*
680 	 *	Verify that none of the pcap handles have dropped packets.
681 	 */
682 	INFO("Interface capture rate:");
683 	for (in_p = this->in;
684 	     in_p;
685 	     in_p = in_p->next) {
686 		if (rs_check_pcap_drop(in_p, conf->stats.interval) < 0) {
687 			ERROR("Muting stats for the next %i milliseconds", conf->stats.timeout);
688 
689 			rs_tv_add_ms(&now, conf->stats.timeout, &stats->quiet);
690 			goto clear;
691 		}
692 	}
693 
694 	if ((stats->quiet.tv_sec + (stats->quiet.tv_usec / 1000000.0)) -
695 	    (now.tv_sec + (now.tv_usec / 1000000.0)) > 0) {
696 		INFO("Stats muted because of warmup, or previous error");
697 		goto clear;
698 	}
699 
700 	/*
701 	 *	Latency stats need a bit more work to calculate the SMA.
702 	 *
703 	 *	No further work is required for codes.
704 	 */
705 	for (i = 0; i < rs_codes_len; i++) {
706 		rs_stats_process_latency(&stats->exchange[rs_useful_codes[i]]);
707 		rs_stats_process_counters(&stats->exchange[rs_useful_codes[i]]);
708 		if (fr_debug_lvl > 0) {
709 			rs_stats_print(&stats->exchange[rs_useful_codes[i]], rs_useful_codes[i]);
710 		}
711 	}
712 
713 #ifdef HAVE_COLLECTDC_H
714 	/*
715 	 *	Update stats in collectd using the complex structures we
716 	 *	initialised earlier.
717 	 */
718 	if ((conf->stats.out == RS_STATS_OUT_COLLECTD) && conf->stats.handle) {
719 		rs_stats_collectd_do_stats(conf, conf->stats.tmpl, &now);
720 	}
721 #endif
722 
723 	clear:
724 	/*
725 	 *	Rinse and repeat...
726 	 */
727 	for (i = 0; i < rs_codes_len; i++) {
728 		memset(&stats->exchange[rs_useful_codes[i]].interval, 0,
729 		       sizeof(stats->exchange[rs_useful_codes[i]].interval));
730 	}
731 
732 	{
733 		static fr_event_t *event;
734 
735 		now.tv_sec += conf->stats.interval;
736 		now.tv_usec = 0;
737 
738 		if (!fr_event_insert(this->list, rs_stats_process, ctx, &now, &event)) {
739 			ERROR("Failed inserting stats interval event");
740 		}
741 	}
742 }
743 
744 
745 /** Update latency statistics for request/response and forwarded packets
746  *
747  */
rs_stats_update_latency(rs_latency_t * stats,struct timeval * latency)748 static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
749 {
750 	double lint;
751 
752 	stats->interval.linked_total++;
753 	/* More useful is this in milliseconds */
754 	lint = (latency->tv_sec + (latency->tv_usec / 1000000.0)) * 1000;
755 	if (lint > stats->interval.latency_high) {
756 		stats->interval.latency_high = lint;
757 	}
758 	if (!stats->interval.latency_low || (lint < stats->interval.latency_low)) {
759 		stats->interval.latency_low = lint;
760 	}
761 	stats->interval.latency_total += lint;
762 
763 }
764 
765 /** Copy a subset of attributes from one list into the other
766  *
767  * Should be O(n) if all the attributes exist.  List must be pre-sorted.
768  */
rs_get_pairs(TALLOC_CTX * ctx,VALUE_PAIR ** out,VALUE_PAIR * vps,DICT_ATTR const * da[],int num)769 static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, DICT_ATTR const *da[], int num)
770 {
771 	vp_cursor_t list_cursor, out_cursor;
772 	VALUE_PAIR *match, *last_match, *copy;
773 	uint64_t count = 0;
774 	int i;
775 
776 	last_match = vps;
777 
778 	fr_cursor_init(&list_cursor, &last_match);
779 	fr_cursor_init(&out_cursor, out);
780 	for (i = 0; i < num; i++) {
781 		match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY);
782 		if (!match) {
783 			fr_cursor_init(&list_cursor, &last_match);
784 			continue;
785 		}
786 
787 		do {
788 			copy = fr_pair_copy(ctx, match);
789 			if (!copy) {
790 				fr_pair_list_free(out);
791 				return -1;
792 			}
793 			fr_cursor_insert(&out_cursor, copy);
794 			last_match = match;
795 
796 			count++;
797 		} while ((match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY)));
798 	}
799 
800 	return count;
801 }
802 
_request_free(rs_request_t * request)803 static int _request_free(rs_request_t *request)
804 {
805 	bool ret;
806 
807 	/*
808 	 *	If were attempting to cleanup the request, and it's no longer in the request_tree
809 	 *	something has gone very badly wrong.
810 	 */
811 	if (request->in_request_tree) {
812 		ret = rbtree_deletebydata(request_tree, request);
813 		RS_ASSERT(ret);
814 	}
815 
816 	if (request->in_link_tree) {
817 		ret = rbtree_deletebydata(link_tree, request);
818 		RS_ASSERT(ret);
819 	}
820 
821 	if (request->event) {
822 		ret = fr_event_delete(events, &request->event);
823 		RS_ASSERT(ret);
824 	}
825 
826 	rad_free(&request->packet);
827 	rad_free(&request->expect);
828 	rad_free(&request->linked);
829 
830 	return 0;
831 }
832 
rs_packet_cleanup(rs_request_t * request)833 static void rs_packet_cleanup(rs_request_t *request)
834 {
835 
836 	RADIUS_PACKET *packet = request->packet;
837 	uint64_t count = request->id;
838 
839 	RS_ASSERT(request->stats_req);
840 	RS_ASSERT(!request->rt_rsp || request->stats_rsp);
841 	RS_ASSERT(packet);
842 
843 	/*
844 	 *	Don't pollute stats or print spurious messages as radsniff closes.
845 	 */
846 	if (cleanup) {
847 		talloc_free(request);
848 		return;
849 	}
850 
851 	if (RIDEBUG_ENABLED()) {
852 		rs_time_print(timestr, sizeof(timestr), &request->when);
853 	}
854 
855 	/*
856 	 *	Were at packet cleanup time which is when the packet was received + timeout
857 	 *	and it's not been linked with a forwarded packet or a response.
858 	 *
859 	 *	We now count it as lost.
860 	 */
861 	if (!request->silent_cleanup) {
862 		if (!request->linked) {
863 			if (!request->stats_req) return;
864 
865 			request->stats_req->interval.lost_total++;
866 
867 			if (conf->event_flags & RS_LOST) {
868 				/* @fixme We should use flags in the request to indicate whether it's been dumped
869 				 * to a PCAP file or logged yet, this simplifies the body logging logic */
870 				rs_packet_print(request, request->id, RS_LOST, request->in, packet, NULL, NULL, false,
871 					        conf->filter_response_vps || !(conf->event_flags & RS_NORMAL));
872 			}
873 		}
874 
875 		if ((request->in->type == PCAP_INTERFACE_IN) && request->logged) {
876 			RDEBUG("Cleaning up request packet ID %i", request->expect->id);
877 		}
878 	}
879 
880 	/*
881 	 *	Now the request is done, we can update the retransmission stats
882 	 */
883 	if (request->rt_req > RS_RETRANSMIT_MAX) {
884 		request->stats_req->interval.rt_total[RS_RETRANSMIT_MAX]++;
885 	} else {
886 		request->stats_req->interval.rt_total[request->rt_req]++;
887 	}
888 
889 	if (request->rt_rsp) {
890 		if (request->rt_rsp > RS_RETRANSMIT_MAX) {
891 			request->stats_rsp->interval.rt_total[RS_RETRANSMIT_MAX]++;
892 		} else {
893 			request->stats_rsp->interval.rt_total[request->rt_rsp]++;
894 		}
895 	}
896 
897 	talloc_free(request);
898 }
899 
_rs_event(void * ctx)900 static void _rs_event(void *ctx)
901 {
902 	rs_request_t *request = talloc_get_type_abort(ctx, rs_request_t);
903 	request->event = NULL;
904 	rs_packet_cleanup(request);
905 }
906 
907 /** Wrapper around fr_packet_cmp to strip off the outer request struct
908  *
909  */
rs_packet_cmp(rs_request_t const * a,rs_request_t const * b)910 static int rs_packet_cmp(rs_request_t const *a, rs_request_t const *b)
911 {
912 	return fr_packet_cmp(a->expect, b->expect);
913 }
914 
rs_response_to_pcap(rs_event_t * event,rs_request_t * request,struct pcap_pkthdr const * header,uint8_t const * data)915 static inline int rs_response_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
916 				      uint8_t const *data)
917 {
918 	if (!event->out) return 0;
919 
920 	/*
921 	 *	If we're filtering by response then the requests then the capture buffer
922 	 *	associated with the request should contain buffered request packets.
923 	 */
924 	if (conf->filter_response && request) {
925 		rs_capture_t *start;
926 
927 		/*
928 		 *	Record the current position in the header
929 		 */
930 		start = request->capture_p;
931 
932 		/*
933 		 *	Buffer hasn't looped set capture_p to the start of the buffer
934 		 */
935 		if (!start->header) request->capture_p = request->capture;
936 
937 		/*
938 		 *	If where capture_p points to, has a header set, write out the
939 		 *	packet to the PCAP file, looping over the buffer until we
940 		 *	hit our start point.
941 		 */
942 		if (request->capture_p->header) do {
943 			pcap_dump((void *)event->out->dumper, request->capture_p->header,
944 				  request->capture_p->data);
945 			TALLOC_FREE(request->capture_p->header);
946 			TALLOC_FREE(request->capture_p->data);
947 
948 			/* Reset the pointer to the start of the circular buffer */
949 			if (request->capture_p++ >=
950 					(request->capture +
951 					 sizeof(request->capture) / sizeof(*request->capture))) {
952 				request->capture_p = request->capture;
953 			}
954 		} while (request->capture_p != start);
955 	}
956 
957 	/*
958 	 *	Now log the response
959 	 */
960 	pcap_dump((void *)event->out->dumper, header, data);
961 
962 	return 0;
963 }
964 
rs_request_to_pcap(rs_event_t * event,rs_request_t * request,struct pcap_pkthdr const * header,uint8_t const * data)965 static inline int rs_request_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
966 				     uint8_t const *data)
967 {
968 	if (!event->out) return 0;
969 
970 	/*
971 	 *	If we're filtering by response, then we need to wait to write out the requests
972 	 */
973 	if (conf->filter_response) {
974 		/* Free the old capture */
975 		if (request->capture_p->header) {
976 			talloc_free(request->capture_p->header);
977 			TALLOC_FREE(request->capture_p->data);
978 		}
979 
980 		if (!(request->capture_p->header = talloc(request, struct pcap_pkthdr))) return -1;
981 		if (!(request->capture_p->data = talloc_array(request, uint8_t, header->caplen))) {
982 			TALLOC_FREE(request->capture_p->header);
983 			return -1;
984 		}
985 		memcpy(request->capture_p->header, header, sizeof(struct pcap_pkthdr));
986 		memcpy(request->capture_p->data, data, header->caplen);
987 
988 		/* Reset the pointer to the start of the circular buffer */
989 		if (++request->capture_p >=
990 				(request->capture +
991 				 sizeof(request->capture) / sizeof(*request->capture))) {
992 			request->capture_p = request->capture;
993 		}
994 		return 0;
995 	}
996 
997 	pcap_dump((void *)event->out->dumper, header, data);
998 
999 	return 0;
1000 }
1001 
1002 /* This is the same as immediately scheduling the cleanup event */
1003 #define RS_CLEANUP_NOW(_x, _s)\
1004 	{\
1005 		_x->silent_cleanup = _s;\
1006 		_x->when = header->ts;\
1007 		rs_packet_cleanup(_x);\
1008 		_x = NULL;\
1009 	} while (0)
1010 
rs_packet_process(uint64_t count,rs_event_t * event,struct pcap_pkthdr const * header,uint8_t const * data)1011 static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
1012 {
1013 	rs_stats_t		*stats = event->stats;
1014 	struct timeval		elapsed = {0, 0};
1015 	struct timeval		latency;
1016 
1017 	/*
1018 	 *	Pointers into the packet data we just received
1019 	 */
1020 	ssize_t len;
1021 	uint8_t const		*p = data;
1022 
1023 	ip_header_t const	*ip = NULL;		/* The IP header */
1024 	ip_header6_t const	*ip6 = NULL;		/* The IPv6 header */
1025 	udp_header_t const	*udp;			/* The UDP header */
1026 	uint8_t			version;		/* IP header version */
1027 	bool			response;		/* Was it a response code */
1028 
1029 	decode_fail_t		reason;			/* Why we failed decoding the packet */
1030 	static uint64_t		captured = 0;
1031 
1032 	rs_status_t		status = RS_NORMAL;	/* Any special conditions (RTX, Unlinked, ID-Reused) */
1033 	RADIUS_PACKET		*current;		/* Current packet were processing */
1034 	rs_request_t		*original = NULL;
1035 
1036 	rs_request_t		search;
1037 
1038 	memset(&search, 0, sizeof(search));
1039 
1040 	if (!start_pcap.tv_sec) {
1041 		start_pcap = header->ts;
1042 	}
1043 
1044 	if (RIDEBUG_ENABLED()) {
1045 		rs_time_print(timestr, sizeof(timestr), &header->ts);
1046 	}
1047 
1048 	len = fr_pcap_link_layer_offset(data, header->caplen, event->in->link_layer);
1049 	if (len < 0) {
1050 		REDEBUG("Failed determining link layer header offset");
1051 		return;
1052 	}
1053 	p += len;
1054 
1055 	version = (p[0] & 0xf0) >> 4;
1056 	switch (version) {
1057 	case 4:
1058 		ip = (ip_header_t const *)p;
1059 		len = (0x0f & ip->ip_vhl) * 4;	/* ip_hl specifies length in 32bit words */
1060 		p += len;
1061 		break;
1062 
1063 	case 6:
1064 		ip6 = (ip_header6_t const *)p;
1065 		p += sizeof(ip_header6_t);
1066 
1067 		break;
1068 
1069 	default:
1070 		REDEBUG("IP version invalid %i", version);
1071 		return;
1072 	}
1073 
1074 	/*
1075 	 *	End of variable length bits, do basic check now to see if packet looks long enough
1076 	 */
1077 	len = (p - data) + sizeof(udp_header_t) + sizeof(radius_packet_t);	/* length value */
1078 	if ((size_t) len > header->caplen) {
1079 		REDEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
1080 			(size_t) len, header->caplen);
1081 		return;
1082 	}
1083 
1084 	/*
1085 	 *	UDP header validation.
1086 	 */
1087 	udp = (udp_header_t const *)p;
1088 	{
1089 		uint16_t udp_len;
1090 		ssize_t diff;
1091 
1092 		udp_len = ntohs(udp->len);
1093 		diff = udp_len - (header->caplen - (p - data));
1094 		/* Truncated data */
1095 		if (diff > 0) {
1096 			REDEBUG("Packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
1097 				diff, udp_len);
1098 			return;
1099 		}
1100 
1101 #if 0
1102 		/*
1103 		 *	It seems many probes add trailing garbage to the end
1104 		 *	of each capture frame.  This has been observed with
1105 		 *	the F5 and Netscout.
1106 		 *
1107 		 *	Leaving the code here in case it's ever needed for
1108 		 *	debugging.
1109 		 */
1110 		else if (diff < 0) {
1111 			REDEBUG("Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
1112 				diff * -1, udp_len);
1113 			return;
1114 		}
1115 #endif
1116 	}
1117 	if ((version == 4) && conf->verify_udp_checksum) {
1118 		uint16_t expected;
1119 
1120 		expected = fr_udp_checksum((uint8_t const *) udp, ntohs(udp->len), udp->checksum,
1121 					   ip->ip_src, ip->ip_dst);
1122 		if (udp->checksum != expected) {
1123 			REDEBUG("UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
1124 				ntohs(udp->checksum), ntohs(expected));
1125 			/* Not a fatal error */
1126 		}
1127 	}
1128 	p += sizeof(udp_header_t);
1129 
1130 	/*
1131 	 *	With artificial talloc memory limits there's a good chance we can
1132 	 *	recover once some requests timeout, so make an effort to deal
1133 	 *	with allocation failures gracefully.
1134 	 */
1135 	current = rad_alloc(conf, false);
1136 	if (!current) {
1137 		REDEBUG("Failed allocating memory to hold decoded packet");
1138 		rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
1139 		return;
1140 	}
1141 
1142 	current->timestamp = header->ts;
1143 	current->data_len = header->caplen - (p - data);
1144 	memcpy(&current->data, &p, sizeof(current->data));
1145 
1146 	/*
1147 	 *	Populate IP/UDP fields from PCAP data
1148 	 */
1149 	if (ip) {
1150 		current->src_ipaddr.af = AF_INET;
1151 		current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
1152 
1153 		current->dst_ipaddr.af = AF_INET;
1154 		current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
1155 	} else {
1156 		current->src_ipaddr.af = AF_INET6;
1157 		memcpy(current->src_ipaddr.ipaddr.ip6addr.s6_addr, ip6->ip_src.s6_addr,
1158 		       sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));
1159 
1160 		current->dst_ipaddr.af = AF_INET6;
1161 		memcpy(current->dst_ipaddr.ipaddr.ip6addr.s6_addr, ip6->ip_dst.s6_addr,
1162 		       sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
1163 	}
1164 
1165 	current->src_port = ntohs(udp->src);
1166 	current->dst_port = ntohs(udp->dst);
1167 
1168 	if (!rad_packet_ok(current, 0, &reason)) {
1169 		REDEBUG("%s", fr_strerror());
1170 		if (conf->event_flags & RS_ERROR) {
1171 			rs_packet_print(NULL, count, RS_ERROR, event->in, current, &elapsed, NULL, false, false);
1172 		}
1173 		rad_free(&current);
1174 
1175 		return;
1176 	}
1177 
1178 	switch (current->code) {
1179 	case PW_CODE_ACCOUNTING_RESPONSE:
1180 	case PW_CODE_ACCESS_REJECT:
1181 	case PW_CODE_ACCESS_ACCEPT:
1182 	case PW_CODE_ACCESS_CHALLENGE:
1183 	case PW_CODE_COA_NAK:
1184 	case PW_CODE_COA_ACK:
1185 	case PW_CODE_DISCONNECT_NAK:
1186 	case PW_CODE_DISCONNECT_ACK:
1187 	case PW_CODE_STATUS_CLIENT:
1188 	{
1189 		/* look for a matching request and use it for decoding */
1190 		search.expect = current;
1191 		original = rbtree_finddata(request_tree, &search);
1192 
1193 		/*
1194 		 *	Verify this code is allowed
1195 		 */
1196 		if (conf->filter_response_code && (conf->filter_response_code != current->code)) {
1197 			drop_response:
1198 			RDEBUG2("Response dropped by filter");
1199 			rad_free(&current);
1200 
1201 			/* We now need to cleanup the original request too */
1202 			if (original) {
1203 				RS_CLEANUP_NOW(original, true);
1204 			}
1205 			return;
1206 		}
1207 
1208 		/*
1209 		 *	Only decode attributes if we want to print them or filter on them
1210 		 *	rad_packet_ok does checks to verify the packet is actually valid.
1211 		 */
1212 		if (conf->decode_attrs) {
1213 			int ret;
1214 			FILE *log_fp = fr_log_fp;
1215 
1216 			fr_log_fp = NULL;
1217 			ret = rad_decode(current, original ? original->expect : NULL, conf->radius_secret);
1218 			fr_log_fp = log_fp;
1219 			if (ret != 0) {
1220 				rad_free(&current);
1221 				REDEBUG("Failed decoding");
1222 				return;
1223 			}
1224 		}
1225 
1226 		/*
1227 		 *	Check if we've managed to link it to a request
1228 		 */
1229 		if (original) {
1230 			/*
1231 			 *	Now verify the packet passes the attribute filter
1232 			 */
1233 			if (conf->filter_response_vps) {
1234 				fr_pair_list_sort(&current->vps, fr_pair_cmp_by_da_tag);
1235 				if (!fr_pair_validate_relaxed(NULL, conf->filter_response_vps, current->vps)) {
1236 					goto drop_response;
1237 				}
1238 			}
1239 
1240 			/*
1241 			 *	Is this a retransmission?
1242 			 */
1243 			if (original->linked) {
1244 				status = RS_RTX;
1245 				original->rt_rsp++;
1246 
1247 				rad_free(&original->linked);
1248 				fr_event_delete(event->list, &original->event);
1249 			/*
1250 			 *	...nope it's the first response to a request.
1251 			 */
1252 			} else {
1253 				original->stats_rsp = &stats->exchange[current->code];
1254 			}
1255 
1256 			/*
1257 			 *	Insert a callback to remove the request and response
1258 			 *	from the tree after the timeout period.
1259 			 *	The delay is so we can detect retransmissions.
1260 			 */
1261 			original->linked = talloc_steal(original, current);
1262 			rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
1263 			if (!fr_event_insert(event->list, _rs_event, original, &original->when,
1264 					     &original->event)) {
1265 				REDEBUG("Failed inserting new event");
1266 				/*
1267 				 *	Delete the original request/event, it's no longer valid
1268 				 *	for statistics.
1269 				 */
1270 				talloc_free(original);
1271 				return;
1272 			}
1273 		/*
1274 		 *	No request seen, or request was dropped by attribute filter
1275 		 */
1276 		} else {
1277 			/*
1278 			 *	If conf->filter_request_vps are set assume the original request was dropped,
1279 			 *	the alternative is maintaining another 'filter', but that adds
1280 			 *	complexity, reduces max capture rate, and is generally a PITA.
1281 			 */
1282 			if (conf->filter_request) {
1283 				rad_free(&current);
1284 				RDEBUG2("Original request dropped by filter");
1285 				return;
1286 			}
1287 
1288 			status = RS_UNLINKED;
1289 			stats->exchange[current->code].interval.unlinked_total++;
1290 		}
1291 
1292 		rs_response_to_pcap(event, original, header, data);
1293 		response = true;
1294 		break;
1295 	}
1296 
1297 	case PW_CODE_ACCOUNTING_REQUEST:
1298 	case PW_CODE_ACCESS_REQUEST:
1299 	case PW_CODE_COA_REQUEST:
1300 	case PW_CODE_DISCONNECT_REQUEST:
1301 	case PW_CODE_STATUS_SERVER:
1302 	{
1303 		/*
1304 		 *	Verify this code is allowed
1305 		 */
1306 		if (conf->filter_request_code && (conf->filter_request_code != current->code)) {
1307 			drop_request:
1308 
1309 			RDEBUG2("Request dropped by filter");
1310 			rad_free(&current);
1311 
1312 			return;
1313 		}
1314 
1315 		/*
1316 		 *	Only decode attributes if we want to print them or filter on them
1317 		 *	rad_packet_ok does checks to verify the packet is actually valid.
1318 		 */
1319 		if (conf->decode_attrs) {
1320 			int ret;
1321 			FILE *log_fp = fr_log_fp;
1322 
1323 			fr_log_fp = NULL;
1324 			ret = rad_decode(current, NULL, conf->radius_secret);
1325 			fr_log_fp = log_fp;
1326 
1327 			if (ret != 0) {
1328 				rad_free(&current);
1329 				REDEBUG("Failed decoding");
1330 				return;
1331 			}
1332 
1333 			fr_pair_list_sort(&current->vps, fr_pair_cmp_by_da_tag);
1334 		}
1335 
1336 		/*
1337 		 *	Save the request for later matching
1338 		 */
1339 		search.expect = rad_alloc_reply(current, current);
1340 		if (!search.expect) {
1341 			REDEBUG("Failed allocating memory to hold expected reply");
1342 			rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
1343 			rad_free(&current);
1344 			return;
1345 		}
1346 		search.expect->code = current->code;
1347 
1348 		if ((conf->link_da_num > 0) && current->vps) {
1349 			int ret;
1350 			ret = rs_get_pairs(current, &search.link_vps, current->vps, conf->link_da,
1351 					   conf->link_da_num);
1352 			if (ret < 0) {
1353 				ERROR("Failed extracting RTX linking pairs from request");
1354 				rad_free(&current);
1355 				return;
1356 			}
1357 		}
1358 
1359 		/*
1360 		 *	If we have linking attributes set, attempt to find a request in the linking tree.
1361 		 */
1362 		if (search.link_vps) {
1363 			rs_request_t *tuple;
1364 
1365 			original = rbtree_finddata(link_tree, &search);
1366 			tuple = rbtree_finddata(request_tree, &search);
1367 
1368 			/*
1369 			 *	If the packet we matched using attributes is not the same
1370 			 *	as the packet in the request tree, then we need to clean up
1371 			 *	the packet in the request tree.
1372 			 */
1373 			if (tuple && (original != tuple)) {
1374 				RS_CLEANUP_NOW(tuple, true);
1375 			}
1376 		/*
1377 		 *	Detect duplicates using the normal 5-tuple of src/dst ips/ports id
1378 		 */
1379 		} else {
1380 			original = rbtree_finddata(request_tree, &search);
1381 			if (original && (memcmp(original->expect->vector, current->vector,
1382 			    			sizeof(original->expect->vector)) != 0)) {
1383 				/*
1384 				 *	ID reused before the request timed out (which may be an issue)...
1385 				 */
1386 				if (!original->linked) {
1387 					status = RS_REUSED;
1388 					stats->exchange[current->code].interval.reused_total++;
1389 					/* Occurs regularly downstream of proxy servers (so don't complain) */
1390 					RS_CLEANUP_NOW(original, true);
1391 				/*
1392 				 *	...and before we saw a response (which may be a bigger issue).
1393 				 */
1394 				} else {
1395 					RS_CLEANUP_NOW(original, false);
1396 				}
1397 				/* else it's a proper RTX with the same src/dst id authenticator/nonce */
1398 			}
1399 		}
1400 
1401 		/*
1402 		 *	Now verify the packet passes the attribute filter
1403 		 */
1404 		if (conf->filter_request_vps) {
1405 			if (!fr_pair_validate_relaxed(NULL, conf->filter_request_vps, current->vps)) {
1406 				goto drop_request;
1407 			}
1408 		}
1409 
1410 		/*
1411 		 *	Is this a retransmission?
1412 		 */
1413 		if (original) {
1414 			status = RS_RTX;
1415 			original->rt_req++;
1416 
1417 			rad_free(&original->packet);
1418 
1419 			/* We may of seen the response, but it may of been lost upstream */
1420 			rad_free(&original->linked);
1421 
1422 			original->packet = talloc_steal(original, current);
1423 
1424 			/* Request may need to be reinserted as the 5 tuple of the response may of changed */
1425 			if (rs_packet_cmp(original, &search) != 0) {
1426 				rbtree_deletebydata(request_tree, original);
1427 			}
1428 
1429 			rad_free(&original->expect);
1430 			original->expect = talloc_steal(original, search.expect);
1431 
1432 			/* Disarm the timer for the cleanup event for the original request */
1433 			fr_event_delete(event->list, &original->event);
1434 		/*
1435 		 *	...nope it's a new request.
1436 		 */
1437 		} else {
1438 			original = talloc_zero(conf, rs_request_t);
1439 			talloc_set_destructor(original, _request_free);
1440 
1441 			original->id = count;
1442 			original->in = event->in;
1443 			original->stats_req = &stats->exchange[current->code];
1444 
1445 			/* Set the packet pointer to the start of the buffer*/
1446 			original->capture_p = original->capture;
1447 
1448 			original->packet = talloc_steal(original, current);
1449 			original->expect = talloc_steal(original, search.expect);
1450 
1451 			if (search.link_vps) {
1452 				bool ret;
1453 				vp_cursor_t cursor;
1454 				VALUE_PAIR *vp;
1455 
1456 				for (vp = fr_cursor_init(&cursor, &search.link_vps);
1457 				     vp;
1458 				     vp = fr_cursor_next(&cursor)) {
1459 					fr_pair_steal(original, search.link_vps);
1460 				}
1461 				original->link_vps = search.link_vps;
1462 
1463 				/* We should never have conflicts */
1464 				ret = rbtree_insert(link_tree, original);
1465 				RS_ASSERT(ret);
1466 				original->in_link_tree = true;
1467 			}
1468 
1469 			/*
1470 			 *	Special case for when were filtering by response,
1471 			 *	we never count any requests as lost, because we
1472 			 *	don't know what the response to that request would
1473 			 *	of been.
1474 			 */
1475 			if (conf->filter_response_vps) {
1476 				original->silent_cleanup = true;
1477 			}
1478 		}
1479 
1480 		if (!original->in_request_tree) {
1481 			bool ret;
1482 
1483 			/* We should never have conflicts */
1484 			ret = rbtree_insert(request_tree, original);
1485 			RS_ASSERT(ret);
1486 			original->in_request_tree = true;
1487 		}
1488 
1489 		/*
1490 		 *	Insert a callback to remove the request from the tree
1491 		 */
1492 		original->packet->timestamp = header->ts;
1493 		rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
1494 		if (!fr_event_insert(event->list, _rs_event, original,
1495 				     &original->when, &original->event)) {
1496 			REDEBUG("Failed inserting new event");
1497 
1498 			talloc_free(original);
1499 			return;
1500 		}
1501 		rs_request_to_pcap(event, original, header, data);
1502 		response = false;
1503 		break;
1504 	}
1505 
1506 	default:
1507 		REDEBUG("Unsupported code %i", current->code);
1508 		rad_free(&current);
1509 
1510 		return;
1511 	}
1512 
1513 	rs_tv_sub(&header->ts, &start_pcap, &elapsed);
1514 
1515 	/*
1516 	 *	Increase received count
1517 	 */
1518 	stats->exchange[current->code].interval.received_total++;
1519 
1520 	/*
1521 	 *	It's a linked response
1522 	 */
1523 	if (original && original->linked) {
1524 		rs_tv_sub(&current->timestamp, &original->packet->timestamp, &latency);
1525 
1526 		/*
1527 		 *	Update stats for both the request and response types.
1528 		 *
1529 		 *	This isn't useful for things like Access-Requests, but will be useful for
1530 		 *	CoA and Disconnect Messages, as we get the average latency across both
1531 		 *	response types.
1532 		 *
1533 		 *	It also justifies allocating PW_CODE_MAX instances of rs_latency_t.
1534 		 */
1535 		rs_stats_update_latency(&stats->exchange[current->code], &latency);
1536 		rs_stats_update_latency(&stats->exchange[original->expect->code], &latency);
1537 
1538 		/*
1539 		 *	Were filtering on response, now print out the full data from the request
1540 		 */
1541 		if (conf->filter_response && RIDEBUG_ENABLED() && (conf->event_flags & RS_NORMAL)) {
1542 			rs_time_print(timestr, sizeof(timestr), &original->packet->timestamp);
1543 			rs_tv_sub(&original->packet->timestamp, &start_pcap, &elapsed);
1544 			rs_packet_print(original, original->id, RS_NORMAL, original->in,
1545 					original->packet, &elapsed, NULL, false, true);
1546 			rs_tv_sub(&header->ts, &start_pcap, &elapsed);
1547 			rs_time_print(timestr, sizeof(timestr), &header->ts);
1548 		}
1549 
1550 		if (conf->event_flags & status) {
1551 			rs_packet_print(original, count, status, event->in, current,
1552 					&elapsed, &latency, response, true);
1553 		}
1554 	/*
1555 	 *	It's the original request
1556 	 *
1557 	 *	If were filtering on responses we can only indicate we received it on response, or timeout.
1558 	 */
1559 	} else if (!conf->filter_response && (conf->event_flags & status)) {
1560 		rs_packet_print(original, original ? original->id : count, status, event->in,
1561 				current, &elapsed, NULL, response, true);
1562 	}
1563 
1564 	fflush(fr_log_fp);
1565 
1566 	/*
1567 	 *	If it's a unlinked response, we need to free it explicitly, as it will
1568 	 *	not be done by the event queue.
1569 	 */
1570 	if (response && !original) {
1571 		rad_free(&current);
1572 	}
1573 
1574 	captured++;
1575 	/*
1576 	 *	We've hit our capture limit, break out of the event loop
1577 	 */
1578 	if ((conf->limit > 0) && (captured >= conf->limit)) {
1579 		INFO("Captured %" PRIu64 " packets, exiting...", captured);
1580 		fr_event_loop_exit(events, 1);
1581 	}
1582 }
1583 
rs_got_packet(fr_event_list_t * el,int fd,void * ctx)1584 static void rs_got_packet(fr_event_list_t *el, int fd, void *ctx)
1585 {
1586 	static uint64_t	count = 0;	/* Packets seen */
1587 	rs_event_t	*event = ctx;
1588 	pcap_t		*handle = event->in->handle;
1589 
1590 	int i;
1591 	int ret;
1592 	const uint8_t *data;
1593 	struct pcap_pkthdr *header;
1594 
1595 	/*
1596 	 *	Consume entire capture, interleaving not currently possible
1597 	 */
1598 	if ((event->in->type == PCAP_FILE_IN) || (event->in->type == PCAP_STDIO_IN)) {
1599 		while (!fr_event_loop_exiting(el)) {
1600 			struct timeval now;
1601 
1602 			ret = pcap_next_ex(handle, &header, &data);
1603 			if (ret == 0) {
1604 				/* No more packets available at this time */
1605 				return;
1606 			}
1607 			if (ret == -2) {
1608 				DEBUG("Done reading packets (%s)", event->in->name);
1609 				fr_event_fd_delete(events, 0, fd);
1610 
1611 				/* Signal pipe takes one slot which is why this is == 1 */
1612 				if (fr_event_list_num_fds(events) == 1) {
1613 					fr_event_loop_exit(events, 1);
1614 				}
1615 
1616 				return;
1617 			}
1618 			if (ret < 0) {
1619 				ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
1620 				return;
1621 			}
1622 
1623 			do {
1624 				now = header->ts;
1625 			} while (fr_event_run(el, &now) == 1);
1626 			count++;
1627 
1628 			rs_packet_process(count, event, header, data);
1629 		}
1630 		return;
1631 	}
1632 
1633 	/*
1634 	 *	Consume multiple packets from the capture buffer.
1635 	 *	We occasionally need to yield to allow events to run.
1636 	 */
1637 	for (i = 0; i < RS_FORCE_YIELD; i++) {
1638 		ret = pcap_next_ex(handle, &header, &data);
1639 		if (ret == 0) {
1640 			/* No more packets available at this time */
1641 			return;
1642 		}
1643 		if (ret < 0) {
1644 			ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
1645 			return;
1646 		}
1647 
1648 		count++;
1649 		rs_packet_process(count, event, header, data);
1650 	}
1651 }
1652 
_rs_event_status(struct timeval * wake)1653 static void _rs_event_status(struct timeval *wake)
1654 {
1655 	if (wake && ((wake->tv_sec != 0) || (wake->tv_usec >= 100000))) {
1656 		DEBUG2("Waking up in %d.%01u seconds.", (int) wake->tv_sec, (unsigned int) wake->tv_usec / 100000);
1657 
1658 		if (RIDEBUG_ENABLED()) {
1659 			rs_time_print(timestr, sizeof(timestr), wake);
1660 		}
1661 	}
1662 }
1663 
1664 /** Compare requests using packet info and lists of attributes
1665  *
1666  */
rs_rtx_cmp(rs_request_t const * a,rs_request_t const * b)1667 static int rs_rtx_cmp(rs_request_t const *a, rs_request_t const *b)
1668 {
1669 	int rcode;
1670 
1671 	RS_ASSERT(a->link_vps);
1672 	RS_ASSERT(b->link_vps);
1673 
1674 	rcode = (int) a->expect->code - (int) b->expect->code;
1675 	if (rcode != 0) return rcode;
1676 
1677 	rcode = a->expect->sockfd - b->expect->sockfd;
1678 	if (rcode != 0) return rcode;
1679 
1680 	rcode = fr_ipaddr_cmp(&a->expect->src_ipaddr, &b->expect->src_ipaddr);
1681 	if (rcode != 0) return rcode;
1682 
1683 	rcode = fr_ipaddr_cmp(&a->expect->dst_ipaddr, &b->expect->dst_ipaddr);
1684 	if (rcode != 0) return rcode;
1685 
1686 	return fr_pair_list_cmp(a->link_vps, b->link_vps);
1687 }
1688 
rs_build_dict_list(DICT_ATTR const ** out,size_t len,char * list)1689 static int rs_build_dict_list(DICT_ATTR const **out, size_t len, char *list)
1690 {
1691 	size_t i = 0;
1692 	char *p, *tok;
1693 
1694 	p = list;
1695 	while ((tok = strsep(&p, "\t ,")) != NULL) {
1696 		DICT_ATTR const *da;
1697 		if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
1698 			continue;
1699 		}
1700 
1701 		if (i == len) {
1702 			ERROR("Too many attributes, maximum allowed is %zu", len);
1703 			return -1;
1704 		}
1705 
1706 		da = dict_attrbyname(tok);
1707 		if (!da) {
1708 			ERROR("Error parsing attribute name \"%s\"", tok);
1709 			return -1;
1710 		}
1711 
1712 		out[i] = da;
1713 		i++;
1714 	}
1715 
1716 	/*
1717 	 *	This allows efficient list comparisons later
1718 	 */
1719 	if (i > 1) fr_quick_sort((void const **)out, 0, i - 1, fr_pointer_cmp);
1720 
1721 	return i;
1722 }
1723 
rs_build_filter(VALUE_PAIR ** out,char const * filter)1724 static int rs_build_filter(VALUE_PAIR **out, char const *filter)
1725 {
1726 	vp_cursor_t cursor;
1727 	VALUE_PAIR *vp;
1728 	FR_TOKEN code;
1729 
1730 	code = fr_pair_list_afrom_str(conf, filter, out);
1731 	if (code == T_INVALID) {
1732 		ERROR("Invalid RADIUS filter \"%s\" (%s)", filter, fr_strerror());
1733 		return -1;
1734 	}
1735 
1736 	if (!*out) {
1737 		ERROR("Empty RADIUS filter '%s'", filter);
1738 		return -1;
1739 	}
1740 
1741 	for (vp = fr_cursor_init(&cursor, out);
1742 	     vp;
1743 	     vp = fr_cursor_next(&cursor)) {
1744 		/*
1745 		 *	xlat expansion isn't supported here
1746 		 */
1747 		if (vp->type == VT_XLAT) {
1748 			vp->type = VT_DATA;
1749 			vp->vp_strvalue = vp->value.xlat;
1750 			vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
1751 		}
1752 	}
1753 
1754 	/*
1755 	 *	This allows efficient list comparisons later
1756 	 */
1757 	fr_pair_list_sort(out, fr_pair_cmp_by_da_tag);
1758 
1759 	return 0;
1760 }
1761 
rs_build_event_flags(int * flags,FR_NAME_NUMBER const * map,char * list)1762 static int rs_build_event_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
1763 {
1764 	size_t i = 0;
1765 	char *p, *tok;
1766 
1767 	p = list;
1768 	while ((tok = strsep(&p, "\t ,")) != NULL) {
1769 		int flag;
1770 
1771 		if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
1772 			continue;
1773 		}
1774 
1775 		*flags |= flag = fr_str2int(map, tok, -1);
1776 		if (flag < 0) {
1777 			ERROR("Invalid flag \"%s\"", tok);
1778 			return -1;
1779 		}
1780 
1781 		i++;
1782 	}
1783 
1784 	return i;
1785 }
1786 
1787 /** Callback for when the request is removed from the request tree
1788  *
1789  * @param request being removed.
1790  */
_unmark_request(void * request)1791 static void _unmark_request(void *request)
1792 {
1793 	rs_request_t *this = request;
1794 	this->in_request_tree = false;
1795 }
1796 
1797 /** Callback for when the request is removed from the link tree
1798  *
1799  * @param request being removed.
1800  */
_unmark_link(void * request)1801 static void _unmark_link(void *request)
1802 {
1803 	rs_request_t *this = request;
1804 	this->in_link_tree = false;
1805 }
1806 
1807 #ifdef HAVE_COLLECTDC_H
1808 /** Re-open the collectd socket
1809  *
1810  */
rs_collectd_reopen(void * ctx)1811 static void rs_collectd_reopen(void *ctx)
1812 {
1813 	fr_event_list_t *list = ctx;
1814 	static fr_event_t *event;
1815 	struct timeval now, when;
1816 
1817 	if (rs_stats_collectd_open(conf) == 0) {
1818 		DEBUG2("Stats output socket (re)opened");
1819 		return;
1820 	}
1821 
1822 	ERROR("Will attempt to re-establish connection in %i ms", RS_SOCKET_REOPEN_DELAY);
1823 
1824 	gettimeofday(&now, NULL);
1825 	rs_tv_add_ms(&now, RS_SOCKET_REOPEN_DELAY, &when);
1826 	if (!fr_event_insert(list, rs_collectd_reopen, list, &when, &event)) {
1827 		ERROR("Failed inserting re-open event");
1828 		RS_ASSERT(0);
1829 	}
1830 }
1831 #endif
1832 
1833 /** Write the last signal to the signal pipe
1834  *
1835  * @param sig raised
1836  */
rs_signal_self(int sig)1837 static void rs_signal_self(int sig)
1838 {
1839 	if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
1840 		ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
1841 		exit(EXIT_FAILURE);
1842 	}
1843 }
1844 
1845 /** Read the last signal from the signal pipe
1846  *
1847  */
rs_signal_action(UNUSED fr_event_list_t * list,int fd,UNUSED void * ctx)1848 static void rs_signal_action(
1849 #ifndef HAVE_COLLECTDC_H
1850 UNUSED
1851 #endif
1852 fr_event_list_t *list, int fd, UNUSED void *ctx)
1853 {
1854 	int sig;
1855 	ssize_t ret;
1856 
1857 	ret = read(fd, &sig, sizeof(sig));
1858 	if (ret < 0) {
1859 		ERROR("Failed reading signal from pipe: %s", fr_syserror(errno));
1860 		exit(EXIT_FAILURE);
1861 	}
1862 
1863 	if (ret != sizeof(sig)) {
1864 		ERROR("Failed reading signal from pipe: "
1865 		      "Expected signal to be %zu bytes but only read %zu byes", sizeof(sig), ret);
1866 		exit(EXIT_FAILURE);
1867 	}
1868 
1869 	switch (sig) {
1870 #ifdef HAVE_COLLECTDC_H
1871 	case SIGPIPE:
1872 		rs_collectd_reopen(list);
1873 		break;
1874 #endif
1875 
1876 	case SIGINT:
1877 	case SIGTERM:
1878 	case SIGQUIT:
1879 		DEBUG2("Signalling event loop to exit");
1880 		fr_event_loop_exit(events, 1);
1881 		break;
1882 
1883 	default:
1884 		ERROR("Unhandled signal %s", strsignal(sig));
1885 		exit(EXIT_FAILURE);
1886 	}
1887 }
1888 
usage(int status)1889 static void NEVER_RETURNS usage(int status)
1890 {
1891 	FILE *output = status ? stderr : stdout;
1892 	fprintf(output, "Usage: radsniff [options][stats options] -- [pcap files]\n");
1893 	fprintf(output, "options:\n");
1894 	fprintf(output, "  -a                    List all interfaces available for capture.\n");
1895 	fprintf(output, "  -c <count>            Number of packets to capture.\n");
1896 	fprintf(output, "  -C                    Enable UDP checksum validation.\n");
1897 	fprintf(output, "  -d <directory>        Set dictionary directory.\n");
1898 	fprintf(output, "  -d <raddb>            Set configuration directory (defaults to " RADDBDIR ").\n");
1899 	fprintf(output, "  -D <dictdir>          Set main dictionary directory (defaults to " DICTDIR ").\n");
1900 	fprintf(output, "  -e <event>[,<event>]  Only log requests with these event flags.\n");
1901 	fprintf(output, "                        Event may be one of the following:\n");
1902 	fprintf(output, "                        - received - a request or response.\n");
1903 	fprintf(output, "                        - norsp    - seen for a request.\n");
1904 	fprintf(output, "                        - rtx      - of a request that we've seen before.\n");
1905 	fprintf(output, "                        - noreq    - could be matched with the response.\n");
1906 	fprintf(output, "                        - reused   - ID too soon.\n");
1907 	fprintf(output, "                        - error    - decoding the packet.\n");
1908 	fprintf(output, "  -f <filter>           PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
1909 	fprintf(output, "  -h                    This help message.\n");
1910 	fprintf(output, "  -i <interface>        Capture packets from interface (defaults to all if supported).\n");
1911 	fprintf(output, "  -I <file>             Read packets from file (overrides input of -F).\n");
1912 	fprintf(output, "  -l <attr>[,<attr>]    Output packet sig and a list of attributes.\n");
1913 	fprintf(output, "  -L <attr>[,<attr>]    Detect retransmissions using these attributes to link requests.\n");
1914 	fprintf(output, "  -m                    Don't put interface(s) into promiscuous mode.\n");
1915 	fprintf(output, "  -p <port>             Filter packets by port (default is 1812).\n");
1916 	fprintf(output, "  -P <pidfile>          Daemonize and write out <pidfile>.\n");
1917 	fprintf(output, "  -q                    Print less debugging information.\n");
1918 	fprintf(output, "  -r <filter>           RADIUS attribute request filter.\n");
1919 	fprintf(output, "  -R <filter>           RADIUS attribute response filter.\n");
1920 	fprintf(output, "  -s <secret>           RADIUS secret.\n");
1921 	fprintf(output, "  -S                    Write PCAP data to stdout.\n");
1922 	fprintf(output, "  -v                    Show program version information.\n");
1923 	fprintf(output, "  -w <file>             Write output packets to file.\n");
1924 	fprintf(output, "  -x                    Print more debugging information.\n");
1925 	fprintf(output, "stats options:\n");
1926 	fprintf(output, "  -W <interval>         Periodically write out statistics every <interval> seconds.\n");
1927 	fprintf(output, "  -T <timeout>          How many milliseconds before the request is counted as lost "
1928 		"(defaults to %i).\n", RS_DEFAULT_TIMEOUT);
1929 #ifdef HAVE_COLLECTDC_H
1930 	fprintf(output, "  -N <prefix>           The instance name passed to the collectd plugin.\n");
1931 	fprintf(output, "  -O <server>           Write statistics to this collectd server.\n");
1932 #endif
1933 	exit(status);
1934 }
1935 
main(int argc,char * argv[])1936 int main(int argc, char *argv[])
1937 {
1938 	fr_pcap_t *in = NULL, *in_p;
1939 	fr_pcap_t **in_head = &in;
1940 	fr_pcap_t *out = NULL;
1941 
1942 	int ret = 1;					/* Exit status */
1943 
1944 	char errbuf[PCAP_ERRBUF_SIZE];			/* Error buffer */
1945 	int port = 1812;
1946 
1947 	char buffer[1024];
1948 
1949 	int opt;
1950 	char const *radius_dir = RADDBDIR;
1951 	char const *dict_dir = DICTDIR;
1952 
1953 	rs_stats_t stats;
1954 
1955 	fr_debug_lvl = 1;
1956 	fr_log_fp = stdout;
1957 
1958 	/*
1959 	 *	Useful if using radsniff as a long running stats daemon
1960 	 */
1961 #ifndef NDEBUG
1962 	if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
1963 		fr_perror("radsniff");
1964 		exit(EXIT_FAILURE);
1965 	}
1966 #endif
1967 
1968 	talloc_set_log_stderr();
1969 
1970 	conf = talloc_zero(NULL, rs_t);
1971 	RS_ASSERT(conf);
1972 
1973 	/*
1974 	 *  We don't really want probes taking down machines
1975 	 */
1976 #ifdef HAVE_TALLOC_SET_MEMLIMIT
1977 	/*
1978 	 *	@fixme causes hang in talloc steal
1979 	 */
1980 	 //talloc_set_memlimit(conf, 524288000);		/* 50 MB */
1981 #endif
1982 
1983 	/*
1984 	 *	Set some defaults
1985 	 */
1986 	conf->print_packet = true;
1987 	conf->limit = 0;
1988 	conf->promiscuous = true;
1989 #ifdef HAVE_COLLECTDC_H
1990 	conf->stats.prefix = RS_DEFAULT_PREFIX;
1991 #endif
1992 	conf->radius_secret = RS_DEFAULT_SECRET;
1993 	conf->logger = NULL;
1994 
1995 #ifdef HAVE_COLLECTDC_H
1996 	conf->stats.prefix = RS_DEFAULT_PREFIX;
1997 #endif
1998 
1999 	/*
2000 	 *  Get options
2001 	 */
2002 	while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
2003 		switch (opt) {
2004 		case 'a':
2005 		{
2006 			pcap_if_t *all_devices = NULL;
2007 			pcap_if_t *dev_p;
2008 
2009 			if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2010 				ERROR("Error getting available capture devices: %s", errbuf);
2011 				goto finish;
2012 			}
2013 
2014 			int i = 1;
2015 			for (dev_p = all_devices;
2016 			     dev_p;
2017 			     dev_p = dev_p->next) {
2018 				INFO("%i.%s", i++, dev_p->name);
2019 			}
2020 			ret = 0;
2021 			pcap_freealldevs(all_devices);
2022 			goto finish;
2023 		}
2024 
2025 		/* super secret option */
2026 		case 'b':
2027 			conf->buffer_pkts = atoi(optarg);
2028 			if (conf->buffer_pkts == 0) {
2029 				ERROR("Invalid buffer length \"%s\"", optarg);
2030 				usage(1);
2031 			}
2032 			break;
2033 
2034 		case 'c':
2035 			conf->limit = atoi(optarg);
2036 			if (conf->limit == 0) {
2037 				ERROR("Invalid number of packets \"%s\"", optarg);
2038 				usage(1);
2039 			}
2040 			break;
2041 
2042 		/* udp checksum */
2043 		case 'C':
2044 			conf->verify_udp_checksum = true;
2045 			break;
2046 
2047 		case 'd':
2048 			radius_dir = optarg;
2049 			break;
2050 
2051 		case 'D':
2052 			dict_dir = optarg;
2053 			break;
2054 
2055 		case 'e':
2056 			if (rs_build_event_flags((int *) &conf->event_flags, rs_events, optarg) < 0) {
2057 				usage(64);
2058 			}
2059 			break;
2060 
2061 		case 'f':
2062 			conf->pcap_filter = optarg;
2063 			break;
2064 
2065 		case 'h':
2066 			usage(0);	/* never returns */
2067 
2068 		case 'i':
2069 			*in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
2070 			if (!*in_head) goto finish;
2071 			in_head = &(*in_head)->next;
2072 			conf->from_dev = true;
2073 			break;
2074 
2075 		case 'I':
2076 			*in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
2077 			if (!*in_head) {
2078 				goto finish;
2079 			}
2080 			in_head = &(*in_head)->next;
2081 			conf->from_file = true;
2082 			break;
2083 
2084 		case 'l':
2085 			conf->list_attributes = optarg;
2086 			break;
2087 
2088 		case 'L':
2089 			conf->link_attributes = optarg;
2090 			break;
2091 
2092 		case 'm':
2093 			conf->promiscuous = false;
2094 			break;
2095 
2096 		case 'p':
2097 			port = atoi(optarg);
2098 			break;
2099 
2100 		case 'P':
2101 			conf->daemonize = true;
2102 			conf->pidfile = optarg;
2103 			break;
2104 
2105 		case 'q':
2106 			if (fr_debug_lvl > 0) {
2107 				fr_debug_lvl--;
2108 			}
2109 			break;
2110 
2111 		case 'r':
2112 			conf->filter_request = optarg;
2113 			break;
2114 
2115 		case 'R':
2116 			conf->filter_response = optarg;
2117 			break;
2118 
2119 		case 's':
2120 			conf->radius_secret = optarg;
2121 			break;
2122 
2123 		case 'S':
2124 			conf->to_stdout = true;
2125 			break;
2126 
2127 		case 'v':
2128 #ifdef HAVE_COLLECTDC_H
2129 			INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
2130 			     lcc_version_string());
2131 #else
2132 			INFO("%s %s", radsniff_version, pcap_lib_version());
2133 #endif
2134 			exit(EXIT_SUCCESS);
2135 
2136 		case 'w':
2137 			out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
2138 			if (!out) {
2139 				ERROR("Failed creating pcap file \"%s\"", optarg);
2140 				exit(EXIT_FAILURE);
2141 			}
2142 			conf->to_file = true;
2143 			break;
2144 
2145 		case 'x':
2146 		case 'X':
2147 			fr_debug_lvl++;
2148 			break;
2149 
2150 		case 'W':
2151 			conf->stats.interval = atoi(optarg);
2152 			conf->print_packet = false;
2153 			if (conf->stats.interval <= 0) {
2154 				ERROR("Stats interval must be > 0");
2155 				usage(64);
2156 			}
2157 			break;
2158 
2159 		case 'T':
2160 			conf->stats.timeout = atoi(optarg);
2161 			if (conf->stats.timeout <= 0) {
2162 				ERROR("Timeout value must be > 0");
2163 				usage(64);
2164 			}
2165 			break;
2166 
2167 #ifdef HAVE_COLLECTDC_H
2168 		case 'N':
2169 			conf->stats.prefix = optarg;
2170 			break;
2171 
2172 		case 'O':
2173 			conf->stats.collectd = optarg;
2174 			conf->stats.out = RS_STATS_OUT_COLLECTD;
2175 			break;
2176 #endif
2177 		default:
2178 			usage(64);
2179 		}
2180 	}
2181 
2182 	/*
2183 	 *	Mismatch between the binary and the libraries it depends on
2184 	 */
2185 	if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
2186 		fr_perror("radsniff");
2187 		exit(EXIT_FAILURE);
2188 	}
2189 
2190 	/* Useful for file globbing */
2191 	while (optind < argc) {
2192 		*in_head = fr_pcap_init(conf, argv[optind], PCAP_FILE_IN);
2193 		if (!*in_head) {
2194 			goto finish;
2195 		}
2196 		in_head = &(*in_head)->next;
2197 		conf->from_file = true;
2198 		optind++;
2199 	}
2200 
2201 	/* Is stdin not a tty? If so it's probably a pipe */
2202 	if (!isatty(fileno(stdin))) {
2203 		conf->from_stdin = true;
2204 	}
2205 
2206 	/* What's the point in specifying -F ?! */
2207 	if (conf->from_stdin && conf->from_file && conf->to_file) {
2208 		usage(64);
2209 	}
2210 
2211 	/* Can't read from both... */
2212 	if (conf->from_file && conf->from_dev) {
2213 		usage(64);
2214 	}
2215 
2216 	/* Reading from file overrides stdin */
2217 	if (conf->from_stdin && (conf->from_file || conf->from_dev)) {
2218 		conf->from_stdin = false;
2219 	}
2220 
2221 	/* Writing to file overrides stdout */
2222 	if (conf->to_file && conf->to_stdout) {
2223 		conf->to_stdout = false;
2224 	}
2225 
2226 	if (conf->to_stdout) {
2227 		out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT);
2228 		if (!out) {
2229 			goto finish;
2230 		}
2231 	}
2232 
2233 	if (conf->from_stdin) {
2234 		*in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN);
2235 		if (!*in_head) {
2236 			goto finish;
2237 		}
2238 		in_head = &(*in_head)->next;
2239 	}
2240 
2241 	if (conf->stats.interval && !conf->stats.out) {
2242 		conf->stats.out = RS_STATS_OUT_STDIO;
2243 	}
2244 
2245 	if (conf->stats.timeout == 0) {
2246 		conf->stats.timeout = RS_DEFAULT_TIMEOUT;
2247 	}
2248 
2249 	/*
2250 	 *	If were writing pcap data, or CSV to stdout we *really* don't want to send
2251 	 *	logging there as well.
2252 	 */
2253 	if (conf->to_stdout || conf->list_attributes) {
2254 		fr_log_fp = stderr;
2255 	}
2256 
2257 	if (conf->list_attributes) {
2258 		conf->logger = rs_packet_print_csv;
2259 	} else if (fr_debug_lvl > 0) {
2260 		conf->logger = rs_packet_print_fancy;
2261 	}
2262 
2263 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
2264 	if (conf->from_stdin || conf->to_stdout) {
2265 		ERROR("PCAP streams not supported");
2266 		goto finish;
2267 	}
2268 #endif
2269 
2270 	if (!conf->pcap_filter) {
2271 		snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
2272 			 port, port + 1, 3799);
2273 		conf->pcap_filter = buffer;
2274 	}
2275 
2276 	if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
2277 		fr_perror("radsniff");
2278 		ret = 64;
2279 		goto finish;
2280 	}
2281 
2282 	if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
2283 		fr_perror("radsniff");
2284 		ret = 64;
2285 		goto finish;
2286 	}
2287 
2288 	fr_strerror();	/* Clear out any non-fatal errors */
2289 
2290 	if (conf->list_attributes) {
2291 		conf->list_da_num = rs_build_dict_list(conf->list_da, sizeof(conf->list_da) / sizeof(*conf->list_da),
2292 						       conf->list_attributes);
2293 		if (conf->list_da_num < 0) {
2294 			usage(64);
2295 		}
2296 		rs_packet_print_csv_header();
2297 	}
2298 
2299 	if (conf->link_attributes) {
2300 		conf->link_da_num = rs_build_dict_list(conf->link_da, sizeof(conf->link_da) / sizeof(*conf->link_da),
2301 						       conf->link_attributes);
2302 		if (conf->link_da_num < 0) {
2303 			usage(64);
2304 		}
2305 
2306 		link_tree = rbtree_create(conf, (rbcmp) rs_rtx_cmp, _unmark_link, 0);
2307 		if (!link_tree) {
2308 			ERROR("Failed creating RTX tree");
2309 			goto finish;
2310 		}
2311 	}
2312 
2313 	if (conf->filter_request) {
2314 		vp_cursor_t cursor;
2315 		VALUE_PAIR *type;
2316 
2317 		if (rs_build_filter(&conf->filter_request_vps, conf->filter_request) < 0) {
2318 			usage(64);
2319 		}
2320 
2321 		fr_cursor_init(&cursor, &conf->filter_request_vps);
2322 		type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
2323 		if (type) {
2324 			fr_cursor_remove(&cursor);
2325 			conf->filter_request_code = type->vp_integer;
2326 			talloc_free(type);
2327 		}
2328 	}
2329 
2330 	if (conf->filter_response) {
2331 		vp_cursor_t cursor;
2332 		VALUE_PAIR *type;
2333 
2334 		if (rs_build_filter(&conf->filter_response_vps, conf->filter_response) < 0) {
2335 			usage(64);
2336 		}
2337 
2338 		fr_cursor_init(&cursor, &conf->filter_response_vps);
2339 		type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
2340 		if (type) {
2341 			fr_cursor_remove(&cursor);
2342 			conf->filter_response_code = type->vp_integer;
2343 			talloc_free(type);
2344 		}
2345 	}
2346 
2347 	/*
2348 	 *	Default to logging and capturing all events
2349 	 */
2350 	if (conf->event_flags == 0) {
2351 		DEBUG("Logging all events");
2352 		memset(&conf->event_flags, 0xff, sizeof(conf->event_flags));
2353 	}
2354 
2355 	/*
2356 	 *	If we need to list attributes, link requests using attributes, filter attributes
2357 	 *	or print the packet contents, we need to decode the attributes.
2358 	 *
2359 	 *	But, if were just logging requests, or graphing packets, we don't need to decode
2360 	 *	attributes.
2361 	 */
2362 	if (conf->list_da_num || conf->link_da_num || conf->filter_response_vps || conf->filter_request_vps ||
2363 	    conf->print_packet) {
2364 		conf->decode_attrs = true;
2365 	}
2366 
2367 	/*
2368 	 *	Setup the request tree
2369 	 */
2370 	request_tree = rbtree_create(conf, (rbcmp) rs_packet_cmp, _unmark_request, 0);
2371 	if (!request_tree) {
2372 		ERROR("Failed creating request tree");
2373 		goto finish;
2374 	}
2375 
2376 	/*
2377 	 *	Get the default capture device
2378 	 */
2379 	if (!conf->from_stdin && !conf->from_file && !conf->from_dev) {
2380 		pcap_if_t *all_devices;			/* List of all devices libpcap can listen on */
2381 		pcap_if_t *dev_p;
2382 
2383 		if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2384 			ERROR("Error getting available capture devices: %s", errbuf);
2385 			goto finish;
2386 		}
2387 
2388 		if (!all_devices) {
2389 			ERROR("No capture files specified and no live interfaces available");
2390 			ret = 64;
2391 			goto finish;
2392 		}
2393 
2394 		for (dev_p = all_devices;
2395 		     dev_p;
2396 		     dev_p = dev_p->next) {
2397 			int link_layer;
2398 
2399 			/* Don't use the any devices, it's horribly broken */
2400 			if (!strcmp(dev_p->name, "any")) continue;
2401 
2402 			link_layer = fr_pcap_if_link_layer(errbuf, dev_p);
2403 			if (link_layer < 0) {
2404 				DEBUG2("Skipping %s: %s", dev_p->name, errbuf);
2405 				continue;
2406 			}
2407 
2408 			if (!fr_pcap_link_layer_supported(link_layer)) {
2409 				DEBUG2("Skipping %s: datalink type %s not supported",
2410 				       dev_p->name, pcap_datalink_val_to_name(link_layer));
2411 				continue;
2412 			}
2413 
2414 			*in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
2415 			in_head = &(*in_head)->next;
2416 		}
2417 		conf->from_auto = true;
2418 		conf->from_dev = true;
2419 
2420 		pcap_freealldevs(all_devices);
2421 
2422 		INFO("Defaulting to capture on all interfaces");
2423 	}
2424 
2425 	/*
2426 	 *	Print captures values which will be used
2427 	 */
2428 	if (fr_debug_lvl > 2) {
2429 		DEBUG2("Sniffing with options:");
2430 		if (conf->from_dev)	{
2431 			char *buff = fr_pcap_device_names(conf, in, ' ');
2432 			DEBUG2("  Device(s)               : [%s]", buff);
2433 			talloc_free(buff);
2434 		}
2435 		if (out) {
2436 			DEBUG2("  Writing to              : [%s]", out->name);
2437 		}
2438 		if (conf->limit > 0)	{
2439 			DEBUG2("  Capture limit (packets) : [%" PRIu64 "]", conf->limit);
2440 		}
2441 			DEBUG2("  PCAP filter             : [%s]", conf->pcap_filter);
2442 			DEBUG2("  RADIUS secret           : [%s]", conf->radius_secret);
2443 
2444 		if (conf->filter_request_code) {
2445 			DEBUG2("  RADIUS request code     : [%s]", fr_packet_codes[conf->filter_request_code]);
2446 		}
2447 
2448 		if (conf->filter_request_vps){
2449 			DEBUG2("  RADIUS request filter   :");
2450 			vp_printlist(fr_log_fp, conf->filter_request_vps);
2451 		}
2452 
2453 		if (conf->filter_response_code) {
2454 			DEBUG2("  RADIUS response code    : [%s]", fr_packet_codes[conf->filter_response_code]);
2455 		}
2456 
2457 		if (conf->filter_response_vps){
2458 			DEBUG2("  RADIUS response filter  :");
2459 			vp_printlist(fr_log_fp, conf->filter_response_vps);
2460 		}
2461 	}
2462 
2463 	/*
2464 	 *	Setup collectd templates
2465 	 */
2466 #ifdef HAVE_COLLECTDC_H
2467 	if (conf->stats.out == RS_STATS_OUT_COLLECTD) {
2468 		size_t i;
2469 		rs_stats_tmpl_t *tmpl, **next;
2470 
2471 		if (rs_stats_collectd_open(conf) < 0) {
2472 			exit(EXIT_FAILURE);
2473 		}
2474 
2475 		next = &conf->stats.tmpl;
2476 
2477 		for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) {
2478 			tmpl = rs_stats_collectd_init_latency(conf, next, conf, "exchanged",
2479 							      &stats.exchange[rs_useful_codes[i]],
2480 							      rs_useful_codes[i]);
2481 			if (!tmpl) {
2482 				ERROR("Error allocating memory for stats template");
2483 				goto finish;
2484 			}
2485 			next = &(tmpl->next);
2486 		}
2487 	}
2488 #endif
2489 
2490 	/*
2491 	 *	This actually opens the capture interfaces/files (we just allocated the memory earlier)
2492 	 */
2493 	{
2494 		fr_pcap_t *tmp;
2495 		fr_pcap_t **tmp_p = &tmp;
2496 
2497 		for (in_p = in;
2498 		     in_p;
2499 		     in_p = in_p->next) {
2500 			in_p->promiscuous = conf->promiscuous;
2501 			in_p->buffer_pkts = conf->buffer_pkts;
2502 			if (fr_pcap_open(in_p) < 0) {
2503 				ERROR("Failed opening pcap handle (%s): %s", in_p->name, fr_strerror());
2504 				if (conf->from_auto || (in_p->type == PCAP_FILE_IN)) {
2505 					continue;
2506 				}
2507 
2508 				goto finish;
2509 			}
2510 
2511 			if (!fr_pcap_link_layer_supported(in_p->link_layer)) {
2512 				ERROR("Failed opening pcap handle (%s): Datalink type %s not supported",
2513 				      in_p->name, pcap_datalink_val_to_name(in_p->link_layer));
2514 				goto finish;
2515 			}
2516 
2517 			if (conf->pcap_filter) {
2518 				if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) {
2519 					ERROR("Failed applying filter");
2520 					goto finish;
2521 				}
2522 			}
2523 
2524 			*tmp_p = in_p;
2525 			tmp_p = &(in_p->next);
2526 		}
2527 		*tmp_p = NULL;
2528 		in = tmp;
2529 
2530 		if (!in) {
2531 			ERROR("No PCAP sources available");
2532 			exit(EXIT_FAILURE);
2533 		}
2534 
2535 		/* Clear any irrelevant errors */
2536 		fr_strerror();
2537 	}
2538 
2539 	/*
2540 	 *	Open our output interface (if we have one);
2541 	 */
2542 	if (out) {
2543 		out->link_layer = -1;	/* Infer output link type from input */
2544 
2545 		for (in_p = in;
2546 		     in_p;
2547 		     in_p = in_p->next) {
2548 			if (out->link_layer < 0) {
2549 				out->link_layer = in_p->link_layer;
2550 				continue;
2551 			}
2552 
2553 			if (out->link_layer != in_p->link_layer) {
2554 				ERROR("Asked to write to output file, but inputs do not have the same link type");
2555 				ret = 64;
2556 				goto finish;
2557 			}
2558 		}
2559 
2560 		RS_ASSERT(out->link_layer >= 0);
2561 
2562 		if (fr_pcap_open(out) < 0) {
2563 			ERROR("Failed opening pcap output (%s): %s", out->name, fr_strerror());
2564 			goto finish;
2565 		}
2566 	}
2567 
2568 	/*
2569 	 *	Setup and enter the main event loop. Who needs libev when you can roll your own...
2570 	 */
2571 	 {
2572 		struct timeval now;
2573 		rs_update_t update;
2574 
2575 		char *buff;
2576 
2577 		memset(&stats, 0, sizeof(stats));
2578 		memset(&update, 0, sizeof(update));
2579 
2580 		events = fr_event_list_create(conf, _rs_event_status);
2581 		if (!events) {
2582 			ERROR();
2583 			goto finish;
2584 		}
2585 
2586 		/*
2587 		 *  Initialise the signal handler pipe
2588 		 */
2589 		if (pipe(self_pipe) < 0) {
2590 			ERROR("Couldn't open signal pipe: %s", fr_syserror(errno));
2591 			exit(EXIT_FAILURE);
2592 		}
2593 
2594 		if (!fr_event_fd_insert(events, 0, self_pipe[0], rs_signal_action, events)) {
2595 			ERROR("Failed inserting signal pipe descriptor: %s", fr_strerror());
2596 			goto finish;
2597 		}
2598 
2599 		/*
2600 		 *  Now add fd's for each of the pcap sessions we opened
2601 		 */
2602 		for (in_p = in;
2603 		     in_p;
2604 		     in_p = in_p->next) {
2605 			rs_event_t *event;
2606 
2607 			event = talloc_zero(events, rs_event_t);
2608 			event->list = events;
2609 			event->in = in_p;
2610 			event->out = out;
2611 			event->stats = &stats;
2612 
2613 			if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) {
2614 				ERROR("Failed inserting file descriptor");
2615 				goto finish;
2616 			}
2617 		}
2618 
2619 		buff = fr_pcap_device_names(conf, in, ' ');
2620 		DEBUG("Sniffing on (%s)", buff);
2621 		talloc_free(buff);
2622 
2623 		gettimeofday(&now, NULL);
2624 
2625 		/*
2626 		 *  Insert our stats processor
2627 		 */
2628 		if (conf->stats.interval) {
2629 			static fr_event_t *event;
2630 
2631 			update.list = events;
2632 			update.stats = &stats;
2633 			update.in = in;
2634 
2635 			now.tv_sec += conf->stats.interval;
2636 			now.tv_usec = 0;
2637 			if (!fr_event_insert(events, rs_stats_process, (void *) &update, &now, &event)) {
2638 				ERROR("Failed inserting stats event");
2639 			}
2640 
2641 			INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout);
2642 			rs_tv_add_ms(&now, conf->stats.timeout, &stats.quiet);
2643 		}
2644 	}
2645 
2646 
2647 	/*
2648 	 *	Do this as late as possible so we can return an error code if something went wrong.
2649 	 */
2650 	if (conf->daemonize) {
2651 		rs_daemonize(conf->pidfile);
2652 	}
2653 
2654 	/*
2655 	 *	Setup signal handlers so we always exit gracefully, ensuring output buffers are always
2656 	 *	flushed.
2657 	 */
2658 	fr_set_signal(SIGPIPE, rs_signal_self);
2659 	fr_set_signal(SIGINT, rs_signal_self);
2660 	fr_set_signal(SIGTERM, rs_signal_self);
2661 #ifdef SIGQUIT
2662 	fr_set_signal(SIGQUIT, rs_signal_self);
2663 #endif
2664 
2665 	fr_event_loop(events);	/* Enter the main event loop */
2666 
2667 	DEBUG("Done sniffing");
2668 
2669 	finish:
2670 
2671 	cleanup = true;
2672 
2673 	/*
2674 	 *	Free all the things! This also closes all the sockets and file descriptors
2675 	 */
2676 	talloc_free(conf);
2677 
2678 	if (conf->daemonize) {
2679 		unlink(conf->pidfile);
2680 	}
2681 
2682 	return ret;
2683 }
2684