The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
radsniff.c
Go to the documentation of this file.
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: bd045ff53ab4d5a253211451fabd349046eae5e6 $
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
27RCSID("$Id: bd045ff53ab4d5a253211451fabd349046eae5e6 $")
28
29#include <fcntl.h>
30#include <time.h>
31#include <math.h>
32
33#include <freeradius-devel/autoconf.h>
34#include <freeradius-devel/radius/list.h>
35#include <freeradius-devel/util/conf.h>
36#include <freeradius-devel/util/event.h>
37#include <freeradius-devel/util/file.h>
38#include <freeradius-devel/util/syserror.h>
39#include <freeradius-devel/util/atexit.h>
40#include <freeradius-devel/util/pair_legacy.h>
41#include <freeradius-devel/util/base16.h>
42#include <freeradius-devel/util/pcap.h>
43#include <freeradius-devel/util/timeval.h>
44
45#ifdef HAVE_COLLECTDC_H
46# include <collectd/client.h>
47#endif
48
49#include "radsniff.h"
50
51#define RS_ASSERT(_x) if (!(_x) && !fr_cond_assert(_x)) exit(1)
52
53static rs_t *conf;
54static struct timeval start_pcap = {0, 0};
55static char timestr[50];
56
58static fr_rb_tree_t *link_tree = NULL;
60static bool cleanup;
61static int packets_count = 1; // Used in '$PATH/${packet}.txt.${count}'
62
63static int self_pipe[2] = {-1, -1}; //!< Signals from sig handlers
64
65static char const *radsniff_version = RADIUSD_VERSION_BUILD("radsniff");
66
67static int rs_useful_codes[] = {
68 FR_RADIUS_CODE_ACCESS_REQUEST, //!< RFC2865 - Authentication request
69 FR_RADIUS_CODE_ACCESS_ACCEPT, //!< RFC2865 - Access-Accept
70 FR_RADIUS_CODE_ACCESS_REJECT, //!< RFC2865 - Access-Reject
71 FR_RADIUS_CODE_ACCOUNTING_REQUEST, //!< RFC2866 - Accounting-Request
72 FR_RADIUS_CODE_ACCOUNTING_RESPONSE, //!< RFC2866 - Accounting-Response
73 FR_RADIUS_CODE_ACCESS_CHALLENGE, //!< RFC2865 - Access-Challenge
74 FR_RADIUS_CODE_STATUS_SERVER, //!< RFC2865/RFC5997 - Status Server (request)
75 FR_RADIUS_CODE_DISCONNECT_REQUEST, //!< RFC3575/RFC5176 - Disconnect-Request
76 FR_RADIUS_CODE_DISCONNECT_ACK, //!< RFC3575/RFC5176 - Disconnect-Ack (positive)
77 FR_RADIUS_CODE_DISCONNECT_NAK, //!< RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
78 FR_RADIUS_CODE_COA_REQUEST, //!< RFC3575/RFC5176 - CoA-Request
79 FR_RADIUS_CODE_COA_ACK, //!< RFC3575/RFC5176 - CoA-Ack (positive)
80 FR_RADIUS_CODE_COA_NAK, //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
81};
82
84 { L("error"), RS_ERROR },
85 { L("noreq"), RS_UNLINKED },
86 { L("norsp"), RS_LOST },
87 { L("received"), RS_NORMAL },
88 { L("reused"), RS_REUSED },
89 { L("rtx"), RS_RTX }
90};
92
94static fr_dict_t const *dict_radius;
95
98 { .out = &dict_freeradius, .proto = "freeradius" },
99 { .out = &dict_radius, .proto = "radius" },
100 { NULL }
101};
102
104
107 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
108 { NULL }
109};
110
111static NEVER_RETURNS void usage(int status);
112
113/** Fork and kill the parent process, writing out our PID
114 *
115 * @param pidfile the PID file to write our PID to
116 */
117static void rs_daemonize(char const *pidfile)
118{
119 FILE *fp;
120 pid_t pid, sid;
121
122 pid = fork();
123 if (pid < 0) {
124 fr_exit_now(EXIT_FAILURE);
125 }
126
127 /*
128 * Kill the parent...
129 */
130 if (pid > 0) {
131 close(self_pipe[0]);
132 close(self_pipe[1]);
133 fr_exit_now(EXIT_SUCCESS);
134 }
135
136 /*
137 * Continue as the child.
138 */
139
140 /* Create a new SID for the child process */
141 sid = setsid();
142 if (sid < 0) {
143 fr_exit_now(EXIT_FAILURE);
144 }
145
146 /*
147 * Change the current working directory. This prevents the current
148 * directory from being locked; hence not being able to remove it.
149 */
150 if ((chdir("/")) < 0) {
151 fr_exit_now(EXIT_FAILURE);
152 }
153
154 /*
155 * And write it AFTER we've forked, so that we write the
156 * correct PID.
157 */
158 fp = fopen(pidfile, "w");
159 if (fp != NULL) {
160 fprintf(fp, "%d\n", (int) sid);
161 fclose(fp);
162 } else {
163 ERROR("Failed creating PID file %s: %s", pidfile, fr_syserror(errno));
164 fr_exit_now(EXIT_FAILURE);
165 }
166
167 /*
168 * Close stdout and stderr if they've not been redirected.
169 */
170 if (isatty(fileno(stdout))) {
171 if (!freopen("/dev/null", "w", stdout)) {
172 fr_exit_now(EXIT_FAILURE);
173 }
174 }
175
176 if (isatty(fileno(stderr))) {
177 if (!freopen("/dev/null", "w", stderr)) {
178 fr_exit_now(EXIT_FAILURE);
179 }
180 }
181}
182
183static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result) {
184 result->tv_sec = start->tv_sec + (interval / 1000);
185 result->tv_usec = start->tv_usec + ((interval % 1000) * 1000);
186
187 if (result->tv_usec > USEC) {
188 result->tv_usec -= USEC;
189 result->tv_sec++;
190 }
191}
192
193static void rs_time_print(char *out, size_t len, struct timeval const *t)
194{
195 size_t ret;
196 struct timeval now;
197 uint32_t usec;
198 struct tm result;
199
200 if (!t) {
202 t = &now;
203 }
204
205 ret = strftime(out, len, "%Y-%m-%d %H:%M:%S", localtime_r(&t->tv_sec, &result));
206 if (ret >= len) {
207 return;
208 }
209
210 usec = t->tv_usec;
211
212 if (usec) {
213 while (usec < 100000) usec *= 10;
214 snprintf(out + ret, len - ret, ".%u", usec);
215 } else {
216 snprintf(out + ret, len - ret, ".000000");
217 }
218}
219
220static size_t rs_snprint_csv(char *out, size_t outlen, char const *in, size_t inlen)
221{
222 char const *start = out;
223 uint8_t const *str = (uint8_t const *) in;
224
225 if (!in) {
226 if (outlen) {
227 *out = '\0';
228 }
229
230 return 0;
231 }
232
233 if (inlen == 0) {
234 inlen = strlen(in);
235 }
236
237 while ((inlen > 0) && (outlen > 2)) {
238 /*
239 * Escape double quotes with... MORE DOUBLE QUOTES!
240 */
241 if (*str == '"') {
242 *out++ = '"';
243 outlen--;
244 }
245
246 /*
247 * Safe chars which require no escaping
248 */
249 if ((*str == '\r') || (*str == '\n') || ((*str >= '\x20') && (*str <= '\x7E'))) {
250 *out++ = *str++;
251 outlen--;
252 inlen--;
253
254 continue;
255 }
256
257 /*
258 * Everything else is dropped
259 */
260 str++;
261 inlen--;
262 }
263 *out = '\0';
264
265 return out - start;
266}
267
269{
270 char buffer[2048];
271 char *p = buffer;
272 int i;
273
274 ssize_t len, s = sizeof(buffer);
275
276 len = strlcpy(p, "\"Status\",\"Count\",\"Time\",\"Latency\",\"Type\",\"Interface\","
277 "\"Src IP\",\"Src Port\",\"Dst IP\",\"Dst Port\",\"ID\",", s);
278 p += len;
279 s -= len;
280
281 if (s <= 0) return;
282
283 for (i = 0; i < conf->list_da_num; i++) {
284 char const *in;
285
286 *p++ = '"';
287 s -= 1;
288 if (s <= 0) return;
289
290 for (in = conf->list_da[i]->name; *in; in++) {
291 *p++ = *in;
292 s -= len;
293 if (s <= 0) return;
294 }
295
296 *p++ = '"';
297 s -= 1;
298 if (s <= 0) return;
299 *p++ = ',';
300 s -= 1;
301 if (s <= 0) return;
302 }
303
304 *--p = '\0';
305
306 fprintf(stdout , "%s\n", buffer);
307}
308
309static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle,
310 fr_packet_t *packet, fr_pair_list_t *list,
311 UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response,
312 bool body)
313{
314 char const *status_str;
315 char buffer[1024];
316 fr_sbuff_t sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
317
318 char src[INET6_ADDRSTRLEN];
319 char dst[INET6_ADDRSTRLEN];
320
321 inet_ntop(packet->socket.inet.src_ipaddr.af, &packet->socket.inet.src_ipaddr.addr, src, sizeof(src));
322 inet_ntop(packet->socket.inet.dst_ipaddr.af, &packet->socket.inet.dst_ipaddr.addr, dst, sizeof(dst));
323
324 status_str = fr_table_str_by_value(rs_events, status, NULL);
325 RS_ASSERT(status_str);
326
327 if (fr_sbuff_in_sprintf(&sbuff, "%s,%" PRIu64 ",%s,", status_str, count, timestr) < 0) return;
328
329 if (latency) {
330 if (fr_sbuff_in_sprintf(&sbuff, "%u.%03u,",
331 (unsigned int) latency->tv_sec,
332 ((unsigned int) latency->tv_usec / 1000)) < 0) return;
333 } else {
334 if (fr_sbuff_in_char(&sbuff, ',') < 0) return;
335 }
336
337 /* Status, Type, Interface, Src, Src port, Dst, Dst port, ID */
338 if (FR_RADIUS_PACKET_CODE_VALID(packet->code)) {
339 if (fr_sbuff_in_sprintf(&sbuff, "%s,%s,%s,%i,%s,%i,%i,",
340 fr_radius_packet_name[packet->code], handle->name,
341 src, packet->socket.inet.src_port, dst, packet->socket.inet.dst_port, packet->id) < 0) return;
342 } else {
343 if (fr_sbuff_in_sprintf(&sbuff, "%u,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
344 src, packet->socket.inet.src_port, dst, packet->socket.inet.dst_port, packet->id) < 0) return;
345 }
346
347 if (body) {
348 int i;
349 fr_pair_t *vp;
350
351 for (i = 0; i < conf->list_da_num; i++) {
352 vp = fr_pair_find_by_da(list, NULL, conf->list_da[i]);
353 if (vp && (vp->vp_length > 0)) {
354 if (conf->list_da[i]->type == FR_TYPE_STRING) {
355 ssize_t slen;
356
357 if (fr_sbuff_in_char(&sbuff, '"') < 0) return;
358
359 slen = rs_snprint_csv(fr_sbuff_current(&sbuff), fr_sbuff_remaining(&sbuff),
360 vp->vp_strvalue, vp->vp_length);
361 if (slen < 0) return;
362 fr_sbuff_advance(&sbuff, (size_t)slen);
363
364 if (fr_sbuff_in_char(&sbuff, '"') < 0) return;
365 } else {
366 if (fr_pair_print_value_quoted(&sbuff, vp, T_BARE_WORD) < 0) return;
367 }
368 }
369
370 if (fr_sbuff_in_char(&sbuff, ',') < 0) return;
371 }
372 } else {
373 if (fr_sbuff_remaining(&sbuff) < (size_t)conf->list_da_num) return;
374
375 memset(fr_sbuff_current(&sbuff), ',', conf->list_da_num);
377 fr_sbuff_terminate(&sbuff);
378 }
379
380 fprintf(stdout , "%s\n", buffer);
381}
382
383static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle,
384 fr_packet_t *packet, fr_pair_list_t *list,
385 struct timeval *elapsed, struct timeval *latency, bool response, bool body)
386{
387 char buffer[2048];
388 char *p = buffer;
389
390 char src[INET6_ADDRSTRLEN];
391 char dst[INET6_ADDRSTRLEN];
392
393 ssize_t len, s = sizeof(buffer);
394
395 inet_ntop(packet->socket.inet.src_ipaddr.af, &packet->socket.inet.src_ipaddr.addr, src, sizeof(src));
396 inet_ntop(packet->socket.inet.dst_ipaddr.af, &packet->socket.inet.dst_ipaddr.addr, dst, sizeof(dst));
397
398 /* Only print out status str if something's not right */
399 if (status != RS_NORMAL) {
400 char const *status_str;
401
402 status_str = fr_table_str_by_value(rs_events, status, NULL);
403 RS_ASSERT(status_str);
404
405 len = snprintf(p, s, "** %s ** ", status_str);
406 p += len;
407 s -= len;
408 if (s <= 0) return;
409 }
410
411 if (FR_RADIUS_PACKET_CODE_VALID(packet->code)) {
412 len = snprintf(p, s, "%s Id %i %s:%s:%d %s %s:%i ",
414 packet->id,
415 handle->name,
416 response ? dst : src,
417 response ? packet->socket.inet.dst_port : packet->socket.inet.src_port,
418 response ? "<-" : "->",
419 response ? src : dst ,
420 response ? packet->socket.inet.src_port : packet->socket.inet.dst_port);
421 } else {
422 len = snprintf(p, s, "%u Id %i %s:%s:%i %s %s:%i ",
423 packet->code,
424 packet->id,
425 handle->name,
426 response ? dst : src,
427 response ? packet->socket.inet.dst_port : packet->socket.inet.src_port,
428 response ? "<-" : "->",
429 response ? src : dst ,
430 response ? packet->socket.inet.src_port : packet->socket.inet.dst_port);
431 }
432 p += len;
433 s -= len;
434 if (s <= 0) return;
435
436 if (elapsed) {
437 len = snprintf(p, s, "+%u.%03u ",
438 (unsigned int) elapsed->tv_sec, ((unsigned int) elapsed->tv_usec / 1000));
439 p += len;
440 s -= len;
441 if (s <= 0) return;
442 }
443
444 if (latency) {
445 len = snprintf(p, s, "+%u.%03u ",
446 (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
447 p += len;
448 s -= len;
449 if (s <= 0) return;
450 }
451
452 *--p = '\0';
453
454 RIDEBUG("%s", buffer);
455
456 if (body) {
457 /*
458 * Print out verbose HEX output
459 */
462 }
463
465 char vector[(RADIUS_AUTH_VECTOR_LENGTH * 2) + 1];
466 fr_sbuff_t vector_sbuff = FR_SBUFF_OUT(vector, sizeof(vector));
467
469 fr_pair_list_log(&default_log, 4, list);
470
471 fr_base16_encode(&vector_sbuff,
473 INFO("\tAuthenticator-Field = 0x%s", fr_sbuff_start(&vector_sbuff));
474 }
475 }
476}
477
478static void rs_packet_save_in_output_dir(uint64_t count, UNUSED rs_status_t status, UNUSED fr_pcap_t *handle,
479 fr_packet_t *packet, fr_pair_list_t *list,
480 UNUSED struct timeval *elapsed, UNUSED struct timeval *latency, bool response, bool body)
481{
482 fr_log_t output_file;
483 char vector[(RADIUS_AUTH_VECTOR_LENGTH * 2) + 1];
484 fr_sbuff_t vector_sbuff = FR_SBUFF_OUT(vector, sizeof(vector));
485 char const *packet_type = response ? "reply" : "request";
486 char filename[2048];
487
488 if (!body) return;
489
490 snprintf(filename, sizeof(filename), "%s/%s.%d.txt", conf->output_dir, packet_type, packets_count);
491
492 if (fr_debug_lvl > 0) {
493 DEBUG2("Saving %s in %s", packet_type, filename);
494 }
495
496 /* ensure to remove existing file */
497 if (fr_unlink(filename) < 0) usage(64);
498
499 if (fr_log_init_file(&output_file, filename) < 0) {
500 ERROR("Failed opening %s output file.", filename);
501 usage(64);
502 }
503
504 output_file.print_level = false;
505 output_file.timestamp = L_TIMESTAMP_OFF;
506
507 /* dump the packet into filesystem */
509 fr_pair_list_log(&output_file, 0, list);
510
511 /* then append the Authenticator-Field */
512 fr_base16_encode(&vector_sbuff,
514
515 fprintf(output_file.handle, "Authenticator-Field = 0x%s\n", fr_sbuff_start(&vector_sbuff));
516
517 if (fr_log_close(&output_file) < 0) {
518 ERROR("Failed closing %s output file.", filename);
519 usage(64);
520 }
521
522 /*
523 * We need to have $PATH/{request,reply}.txt with the same ID
524 * to be possible cross the packets.
525 */
526 if ((count % 2) == 0) packets_count++;
527}
528
529static inline void rs_packet_print(rs_request_t *request, uint64_t count, rs_status_t status, fr_pcap_t *handle,
530 fr_packet_t *packet, fr_pair_list_t *list,
531 struct timeval *elapsed, struct timeval *latency,
532 bool response, bool body)
533{
534 if (!conf->logger) return;
535
536 if (request) request->logged = true;
537 conf->logger(count, status, handle, packet, list, elapsed, latency, response, body);
538}
539
540/** Query libpcap to see if it dropped any packets
541 *
542 * We need to check to see if libpcap dropped any packets and if it did, we need to stop stats output for long
543 * enough for inaccurate statistics to be cleared out.
544 *
545 * @param in pcap handle to check.
546 * @return
547 * - 0 No drops.
548 * - -1 We couldn't check.
549 * - -2 Dropped because of buffer exhaustion.
550 * - -3 Dropped because of NIC.
551 */
552static int rs_check_pcap_drop(fr_pcap_t *in)
553{
554 int ret = 0;
555 struct pcap_stat pstats;
556
557 if (pcap_stats(in->handle, &pstats) != 0) {
558 ERROR("%s failed retrieving pcap stats: %s", in->name, pcap_geterr(in->handle));
559 return -1;
560 }
561
562 if (pstats.ps_drop - in->pstats.ps_drop > 0) {
563 ERROR("%s dropped %i packets: Buffer exhaustion", in->name, pstats.ps_drop - in->pstats.ps_drop);
564 ret = -2;
565 }
566
567 if (pstats.ps_ifdrop - in->pstats.ps_ifdrop > 0) {
568 ERROR("%s dropped %i packets: Interface", in->name, pstats.ps_ifdrop - in->pstats.ps_ifdrop);
569 ret = -3;
570 }
571
572 in->pstats = pstats;
573
574 return ret;
575}
576
577/** Update smoothed average
578 *
579 */
581{
582 /*
583 * If we didn't link any packets during this interval, we don't have a value to return.
584 * returning 0 is misleading as it would be like saying the latency had dropped to 0.
585 * We instead set NaN which libcollectd converts to a 'U' or unknown value.
586 *
587 * This will cause gaps in graphs, but is completely legitimate as we are missing data.
588 * This is unfortunately an effect of being just a passive observer.
589 */
590 if (stats->interval.linked_total == 0) {
591 double unk = strtod("NAN()", (char **) NULL);
592
593 stats->interval.latency_average = unk;
594 stats->interval.latency_high = unk;
595 stats->interval.latency_low = unk;
596
597 /*
598 * We've not yet been able to determine latency, so latency_smoothed is also NaN
599 */
600 if (stats->latency_smoothed_count == 0) {
601 stats->latency_smoothed = unk;
602 }
603 return;
604 }
605
606 if (stats->interval.linked_total && stats->interval.latency_total) {
607 stats->interval.latency_average = (stats->interval.latency_total / stats->interval.linked_total);
608 }
609
610 if (isnan((long double)stats->latency_smoothed)) {
611 stats->latency_smoothed = 0;
612 }
613 if (stats->interval.latency_average > 0) {
614 stats->latency_smoothed_count++;
615 stats->latency_smoothed += ((stats->interval.latency_average - stats->latency_smoothed) /
616 ((stats->latency_smoothed_count < 100) ? stats->latency_smoothed_count : 100));
617 }
618}
619
621{
622 int i;
623
624 stats->interval.received = ((long double) stats->interval.received_total) / conf->stats.interval;
625 stats->interval.linked = ((long double) stats->interval.linked_total) / conf->stats.interval;
626 stats->interval.unlinked = ((long double) stats->interval.unlinked_total) / conf->stats.interval;
627 stats->interval.reused = ((long double) stats->interval.reused_total) / conf->stats.interval;
628 stats->interval.lost = ((long double) stats->interval.lost_total) / conf->stats.interval;
629
630 for (i = 1; i < RS_RETRANSMIT_MAX; i++) {
631 stats->interval.rt[i] = ((long double) stats->interval.rt_total[i]) / conf->stats.interval;
632 }
633}
634
636{
637 int i;
638 bool have_rt = false;
639
640 for (i = 1; i <= RS_RETRANSMIT_MAX; i++) if (stats->interval.rt[i]) have_rt = true;
641
642 if (!stats->interval.received && !have_rt && !stats->interval.reused) return;
643
644 if (stats->interval.received || stats->interval.linked) {
645 INFO("%s counters:", fr_radius_packet_name[code]);
646 if (stats->interval.received > 0) {
647 INFO("\tTotal : %.3lf/s" , stats->interval.received);
648 }
649 }
650
651 if (stats->interval.linked > 0) {
652 INFO("\tLinked : %.3lf/s", stats->interval.linked);
653 INFO("\tUnlinked : %.3lf/s", stats->interval.unlinked);
654 INFO("%s latency:", fr_radius_packet_name[code]);
655 INFO("\tHigh : %.3lfms", stats->interval.latency_high);
656 INFO("\tLow : %.3lfms", stats->interval.latency_low);
657 INFO("\tAverage : %.3lfms", stats->interval.latency_average);
658 INFO("\tMA : %.3lfms", stats->latency_smoothed);
659 }
660
661 if (have_rt || stats->interval.lost || stats->interval.reused) {
662 INFO("%s retransmits & loss:", fr_radius_packet_name[code]);
663
664 if (stats->interval.lost) INFO("\tLost : %.3lf/s", stats->interval.lost);
665 if (stats->interval.reused) INFO("\tID Reused : %.3lf/s", stats->interval.reused);
666
667 for (i = 1; i <= RS_RETRANSMIT_MAX; i++) {
668 if (!stats->interval.rt[i]) continue;
669
670 if (i != RS_RETRANSMIT_MAX) {
671 INFO("\tRT (%i) : %.3lf/s", i, stats->interval.rt[i]);
672 } else {
673 INFO("\tRT (%i+) : %.3lf/s", i, stats->interval.rt[i]);
674 }
675 }
676 }
677}
678
679static void rs_stats_print_fancy(rs_update_t *this, rs_stats_t *stats, struct timeval *now)
680{
681 fr_pcap_t *in_p;
682 size_t i;
683 size_t rs_codes_len = (NUM_ELEMENTS(rs_useful_codes));
684
685 /*
686 * Clear and reset the screen
687 */
688 INFO("\x1b[0;0f");
689 INFO("\x1b[2J");
690
691 if ((stats->quiet.tv_sec + (stats->quiet.tv_usec / 1000000.0)) -
692 (now->tv_sec + (now->tv_usec / 1000000.0)) > 0) {
693 INFO("Stats muted because of warmup, or previous error");
694 return;
695 }
696
697 INFO("######### Stats Iteration %i #########", stats->intervals);
698
699 if (this->in) INFO("Interface capture rate:");
700 for (in_p = this->in;
701 in_p;
702 in_p = in_p->next) {
703 struct pcap_stat pstats;
704
705 if (pcap_stats(in_p->handle, &pstats) != 0) {
706 ERROR("%s failed retrieving pcap stats: %s", in_p->name, pcap_geterr(in_p->handle));
707 return;
708 }
709
710 INFO("\t%s%*s: %.3lf/s", in_p->name, (int) (10 - strlen(in_p->name)), "",
711 ((double) (pstats.ps_recv - in_p->pstats.ps_recv)) / conf->stats.interval);
712 }
713
714 /*
715 * Latency stats need a bit more work to calculate the SMA.
716 *
717 * No further work is required for codes.
718 */
719 for (i = 0; i < rs_codes_len; i++) {
720 if (fr_debug_lvl > 0) {
722 }
723 }
724}
725
727{
728 fr_pcap_t *in_p;
729 size_t rs_codes_len = (NUM_ELEMENTS(rs_useful_codes));
730 size_t i;
731 int j;
732
733 fprintf(stdout, "\"Iteration\"");
734
735 for (in_p = this->in; in_p; in_p = in_p->next) {
736 fprintf(stdout, ",\"%s PPS\"", in_p->name);
737 }
738
739 for (i = 0; i < rs_codes_len; i++) {
741
742 fprintf(stdout,
743 ",\"%s received/s\""
744 ",\"%s linked/s\""
745 ",\"%s unlinked/s\""
746 ",\"%s lat high (ms)\""
747 ",\"%s lat low (ms)\""
748 ",\"%s lat avg (ms)\""
749 ",\"%s lat ma (ms)\""
750 ",\"%s lost/s\""
751 ",\"%s reused/s\"",
752 name,
753 name,
754 name,
755 name,
756 name,
757 name,
758 name,
759 name,
760 name);
761
762 for (j = 1; j <= RS_RETRANSMIT_MAX; j++) {
763 if (j != RS_RETRANSMIT_MAX) {
764 fprintf(stdout, ",\"%s rtx (%i)\"", name, j);
765 } else {
766 fprintf(stdout, ",\"%s rtx (%i+)\"", name, j);
767 }
768 }
769 }
770
771 fprintf(stdout , "\n");
772}
773
774static ssize_t rs_stats_print_code_csv(char *out, size_t outlen, rs_latency_t *stats)
775{
776 size_t i;
777 char *p = out, *end = out + outlen;
778
779 p += snprintf(out, outlen, ",%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf",
780 stats->interval.received,
781 stats->interval.linked,
782 stats->interval.unlinked,
783 stats->interval.latency_high,
784 stats->interval.latency_low,
785 stats->interval.latency_average,
786 stats->latency_smoothed,
787 stats->interval.lost,
788 stats->interval.reused);
789 if (p >= end) return -1;
790
791 for (i = 1; i <= RS_RETRANSMIT_MAX; i++) {
792 p += snprintf(p, outlen - (p - out), ",%.3lf", stats->interval.rt[i]);
793 if (p >= end) return -1;
794 }
795
796 return p - out;
797}
798
799static void rs_stats_print_csv(rs_update_t *this, rs_stats_t *stats, UNUSED struct timeval *now)
800{
801 char buffer[2048], *p = buffer, *end = buffer + sizeof(buffer);
802 fr_pcap_t *in_p;
803 size_t i;
804 size_t rs_codes_len = (NUM_ELEMENTS(rs_useful_codes));
805
806 p += snprintf(buffer, sizeof(buffer) - (p - buffer), "%i", stats->intervals);
807 if (p >= end) {
808 oob:
809 ERROR("Exceeded line buffer size");
810 return;
811 }
812
813 for (in_p = this->in;
814 in_p;
815 in_p = in_p->next) {
816 struct pcap_stat pstats;
817
818 if (pcap_stats(in_p->handle, &pstats) != 0) {
819 ERROR("%s failed retrieving pcap stats: %s", in_p->name, pcap_geterr(in_p->handle));
820 return;
821 }
822
823 p += snprintf(p, sizeof(buffer) - (p - buffer), ",%.3lf",
824 ((double) (pstats.ps_recv - in_p->pstats.ps_recv)) / conf->stats.interval);
825 if (p >= end) goto oob;
826 }
827
828 for (i = 0; i < rs_codes_len; i++) {
829 ssize_t slen;
830
831 slen = rs_stats_print_code_csv(p, sizeof(buffer) - (p - buffer), &stats->exchange[rs_useful_codes[i]]);
832 if (slen < 0) goto oob;
833
834 p += (size_t)slen;
835 if (p >= end) goto oob;
836 }
837
838 fprintf(stdout , "%s\n", buffer);
839}
840
841/** Process stats for a single interval
842 *
843 */
844static void rs_stats_process(fr_timer_list_t *tl, fr_time_t now_t, void *ctx)
845{
846 size_t i;
847 size_t rs_codes_len = (NUM_ELEMENTS(rs_useful_codes));
848 fr_pcap_t *in_p;
849 rs_update_t *this = ctx;
850 rs_stats_t *stats = this->stats;
851 struct timeval now;
852
853 now = fr_time_to_timeval(now_t);
854
855 if (!this->done_header) {
856 if (this->head) this->head(this);
857 this->done_header = true;
858 }
859
860 stats->intervals++;
861
862 for (in_p = this->in;
863 in_p;
864 in_p = in_p->next) {
865 if (rs_check_pcap_drop(in_p) < 0) {
866 ERROR("Muting stats for the next %i milliseconds", conf->stats.timeout);
867
868 rs_tv_add_ms(&now, conf->stats.timeout, &stats->quiet);
869 goto clear;
870 }
871 }
872
873 /*
874 * Stats temporarily muted
875 */
876 if ((stats->quiet.tv_sec + (stats->quiet.tv_usec / 1000000.0)) -
877 (now.tv_sec + (now.tv_usec / 1000000.0)) > 0) goto clear;
878
879 for (i = 0; i < rs_codes_len; i++) {
882 }
883
884 if (this->body) this->body(this, stats, &now);
885
886#ifdef HAVE_COLLECTDC_H
887 /*
888 * Update stats in collectd using the complex structures we
889 * initialised earlier.
890 */
891 if ((conf->stats.out == RS_STATS_OUT_COLLECTD) && conf->stats.handle) {
892 rs_stats_collectd_do_stats(conf, conf->stats.tmpl, &now);
893 }
894#endif
895
896clear:
897 /*
898 * Rinse and repeat...
899 */
900 for (i = 0; i < rs_codes_len; i++) {
901 memset(&stats->exchange[rs_useful_codes[i]].interval, 0,
902 sizeof(stats->exchange[rs_useful_codes[i]].interval));
903 }
904
905 {
906 static fr_timer_t *event;
907
908 now.tv_sec += conf->stats.interval;
909 now.tv_usec = 0;
910
911 if (fr_timer_at(NULL, tl, &event,
913 false, rs_stats_process, ctx) < 0) {
914 ERROR("Failed inserting stats interval event");
915 }
916 }
917}
918
919
920/** Update latency statistics for request/response and forwarded packets
921 *
922 */
923static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
924{
925 double lint;
926
927 stats->interval.linked_total++;
928 /* More useful is this in milliseconds */
929 lint = (latency->tv_sec + (latency->tv_usec / 1000000.0)) * 1000;
930 if (lint > stats->interval.latency_high) {
931 stats->interval.latency_high = lint;
932 }
933 if (!stats->interval.latency_low || (lint < stats->interval.latency_low)) {
934 stats->interval.latency_low = lint;
935 }
936 stats->interval.latency_total += (long double) lint;
937
938}
939
941 fr_pcap_t *in, struct timeval *now, bool live)
942{
943 static fr_timer_t *event;
944 static rs_update_t update;
945
946 memset(&update, 0, sizeof(update));
947
948 update.list = el;
949 update.stats = stats;
950 update.in = in;
951
952 switch (conf->stats.out) {
953 default:
955 update.head = NULL;
956 update.body = rs_stats_print_fancy;
957 break;
958
961 update.body = rs_stats_print_csv;
962 break;
963
964#ifdef HAVE_COLLECTDC_H
965 case RS_STATS_OUT_COLLECTD:
966 update.head = NULL;
967 update.body = NULL;
968 break;
969#endif
970 }
971 /*
972 * Set the first time we print stats
973 */
974 now->tv_sec += conf->stats.interval;
975 now->tv_usec = 0;
976
977 if (live) {
978 INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout);
979 rs_tv_add_ms(now, conf->stats.timeout, &(stats->quiet));
980 }
981
982 if (fr_timer_at(NULL, events->tl, (void *) &event,
984 false, rs_stats_process, &update) < 0) {
985 ERROR("Failed inserting stats event");
986 return -1;
987 }
988
989 return 0;
990}
991
992/** Copy a subset of attributes from one list into the other
993 *
994 * Should be O(n) if all the attributes exist. List must be pre-sorted.
995 */
996static int rs_get_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_pair_list_t *vps, fr_dict_attr_t const *da[], int num)
997{
998 fr_dcursor_t list_cursor;
999 fr_pair_t *match, *copy;
1000 fr_pair_t *last_match = NULL;
1001 uint64_t count = 0;
1002 int i;
1003
1004 last_match = fr_pair_list_head(vps);
1005
1006 fr_pair_dcursor_init(&list_cursor, vps);
1007 for (i = 0; i < num; i++) {
1008 match = fr_dcursor_filter_next(&list_cursor, fr_pair_matches_da, da[i]);
1009 if (!match) {
1010 fr_dcursor_set_current(&list_cursor, last_match);
1011 continue;
1012 }
1013
1014 do {
1015 copy = fr_pair_copy(ctx, match);
1016 if (!copy) {
1018 return -1;
1019 }
1020 fr_pair_append(out, copy);
1021 last_match = match;
1022
1023 count++;
1024 } while ((match = fr_dcursor_filter_next(&list_cursor, fr_pair_matches_da, da[i])));
1025 }
1026
1027 return count;
1028}
1029
1030static int _request_free(rs_request_t *request)
1031{
1032 int ret;
1033
1034 /*
1035 * If we're attempting to cleanup the request, and it's no longer in the request_tree
1036 * something has gone very badly wrong.
1037 */
1038 if (request->in_request_tree) {
1039 ret = fr_rb_delete(request_tree, request);
1040 RS_ASSERT(ret);
1041 }
1042
1043 if (request->in_link_tree) {
1044 ret = fr_rb_delete(link_tree, request);
1045 RS_ASSERT(ret);
1046 }
1047
1048 if (request->event) {
1049 ret = fr_timer_delete(&request->event);
1050 if (ret < 0) {
1051 fr_perror("Failed deleting timer");
1052 RS_ASSERT(0 == 1);
1053 }
1054 }
1055
1056 fr_packet_free(&request->packet);
1057 fr_packet_free(&request->expect);
1058 fr_packet_free(&request->linked);
1059
1060 return 0;
1061}
1062
1063static void rs_packet_cleanup(rs_request_t *request)
1064{
1065
1066 uint64_t count = request->id;
1067
1068 RS_ASSERT(request->stats_req);
1069 RS_ASSERT(!request->rt_rsp || request->stats_rsp);
1070 RS_ASSERT(request->packet);
1071
1072 /*
1073 * Don't pollute stats or print spurious messages as radsniff closes.
1074 */
1075 if (cleanup) {
1076 talloc_free(request);
1077 return;
1078 }
1079
1080 if (RIDEBUG_ENABLED()) {
1081 rs_time_print(timestr, sizeof(timestr), &request->when);
1082 }
1083
1084 /*
1085 * We're at packet cleanup time which is when the packet was received + timeout
1086 * and it's not been linked with a forwarded packet or a response.
1087 *
1088 * We now count it as lost.
1089 */
1090 if (!request->silent_cleanup) {
1091 if (!request->linked) {
1092 if (!request->stats_req) return;
1093
1094 request->stats_req->interval.lost_total++;
1095
1096 if (conf->event_flags & RS_LOST) {
1097 /* @fixme We should use flags in the request to indicate whether it's been dumped
1098 * to a PCAP file or logged yet, this simplifies the body logging logic */
1099 rs_packet_print(request, request->id, RS_LOST, request->in,
1100 request->packet, &request->packet_vps,
1101 NULL, NULL, false,
1103 !(conf->event_flags & RS_NORMAL));
1104 }
1105 }
1106
1107 if ((request->in->type == PCAP_INTERFACE_IN) && request->logged) {
1108 RDEBUG("Cleaning up request packet ID %i", request->expect->id);
1109 }
1110 }
1111
1112 /*
1113 * Now the request is done, we can update the retransmission stats
1114 */
1115 if (request->rt_req) {
1116 if (request->rt_req > RS_RETRANSMIT_MAX) {
1117 request->stats_req->interval.rt_total[RS_RETRANSMIT_MAX]++;
1118 } else {
1119 request->stats_req->interval.rt_total[request->rt_req]++;
1120 }
1121 }
1122
1123 if (request->rt_rsp) {
1124 if (request->rt_rsp > RS_RETRANSMIT_MAX) {
1125 request->stats_rsp->interval.rt_total[RS_RETRANSMIT_MAX]++;
1126 } else {
1127 request->stats_rsp->interval.rt_total[request->rt_rsp]++;
1128 }
1129 }
1130
1131 talloc_free(request);
1132}
1133
1134static void _rs_event(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *ctx)
1135{
1136 rs_request_t *request = talloc_get_type_abort(ctx, rs_request_t);
1137
1138 rs_packet_cleanup(request);
1139}
1140
1141/** Wrapper around fr_packet_cmp to strip off the outer request struct
1142 *
1143 */
1144static int8_t rs_packet_cmp(void const *one, void const *two)
1145{
1146 rs_request_t const *a = one;
1147 rs_request_t const *b = two;
1148
1149 return fr_packet_cmp(a->expect, b->expect);
1150}
1151
1152static inline int rs_response_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
1153 uint8_t const *data)
1154{
1155 if (!event->out) return 0;
1156
1157 /*
1158 * If we're filtering by response then the requests then the capture buffer
1159 * associated with the request should contain buffered request packets.
1160 */
1161 if (conf->filter_response && request) {
1162 rs_capture_t *start;
1163
1164 /*
1165 * Record the current position in the header
1166 */
1167 start = request->capture_p;
1168
1169 /*
1170 * Buffer hasn't looped set capture_p to the start of the buffer
1171 */
1172 if (!start->header) request->capture_p = request->capture;
1173
1174 /*
1175 * If where capture_p points to, has a header set, write out the
1176 * packet to the PCAP file, looping over the buffer until we
1177 * hit our start point.
1178 */
1179 if (request->capture_p->header) do {
1180 pcap_dump((void *)event->out->dumper, request->capture_p->header,
1181 request->capture_p->data);
1182 TALLOC_FREE(request->capture_p->header);
1183 TALLOC_FREE(request->capture_p->data);
1184
1185 /* Reset the pointer to the start of the circular buffer */
1186 if (request->capture_p++ >=
1187 (request->capture +
1188 NUM_ELEMENTS(request->capture))) {
1189 request->capture_p = request->capture;
1190 }
1191 } while (request->capture_p != start);
1192 }
1193
1194 /*
1195 * Now log the response
1196 */
1197 pcap_dump((void *)event->out->dumper, header, data);
1198
1199 return 0;
1200}
1201
1202static inline int rs_request_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header,
1203 uint8_t const *data)
1204{
1205 if (!event->out) return 0;
1206
1207 /*
1208 * If we're filtering by response, then we need to wait to write out the requests
1209 */
1210 if (conf->filter_response) {
1211 /* Free the old capture */
1212 if (request->capture_p->header) {
1213 talloc_free(request->capture_p->header);
1214 TALLOC_FREE(request->capture_p->data);
1215 }
1216
1217 if (!(request->capture_p->header = talloc(request, struct pcap_pkthdr))) return -1;
1218 if (!(request->capture_p->data = talloc_array(request, uint8_t, header->caplen))) {
1219 TALLOC_FREE(request->capture_p->header);
1220 return -1;
1221 }
1222 memcpy(request->capture_p->header, header, sizeof(struct pcap_pkthdr));
1223 memcpy(request->capture_p->data, data, header->caplen);
1224
1225 /* Reset the pointer to the start of the circular buffer */
1226 if (++request->capture_p >=
1227 (request->capture +
1228 NUM_ELEMENTS(request->capture))) {
1229 request->capture_p = request->capture;
1230 }
1231 return 0;
1232 }
1233
1234 pcap_dump((void *)event->out->dumper, header, data);
1235
1236 return 0;
1237}
1238
1239/* This is the same as immediately scheduling the cleanup event */
1240#define RS_CLEANUP_NOW(_x, _s)\
1241 {\
1242 _x->silent_cleanup = _s;\
1243 _x->when = header->ts;\
1244 rs_packet_cleanup(_x);\
1245 _x = NULL;\
1246 } while (0)
1247
1248static rs_request_t *rs_request_alloc(TALLOC_CTX *ctx)
1249{
1250 rs_request_t *original;
1251
1252 original = talloc_zero(ctx, rs_request_t);
1253 talloc_set_destructor(original, _request_free);
1254
1255 fr_pair_list_init(&original->packet_vps);
1256 fr_pair_list_init(&original->expect_vps);
1257 fr_pair_list_init(&original->link_vps);
1258
1259 return original;
1260}
1261
1263
1264static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
1265{
1266 rs_stats_t *stats = event->stats;
1267 struct timeval elapsed = {0, 0};
1268 struct timeval latency;
1269
1270 /*
1271 * Pointers into the packet data we just received
1272 */
1273 ssize_t len;
1274 uint8_t const *p = data;
1275
1276 ip_header_t const *ip = NULL; /* The IP header */
1277 ip_header6_t const *ip6 = NULL; /* The IPv6 header */
1278 udp_header_t const *udp; /* The UDP header */
1279 uint8_t version; /* IP header version */
1280 bool response; /* Was it a response code */
1281
1282 fr_radius_decode_fail_t reason; /* Why we failed decoding the packet */
1283 static uint64_t captured = 0;
1284
1285 rs_status_t status = RS_NORMAL; /* Any special conditions (RTX, Unlinked, ID-Reused) */
1286 fr_packet_t *packet; /* Current packet were processing */
1287 rs_request_t *original = NULL;
1288 fr_pair_list_t decoded;
1289
1290 rs_request_t search;
1291
1292 fr_pair_list_init(&decoded);
1293
1294 memset(&search, 0, sizeof(search));
1297 fr_pair_list_init(&search.link_vps);
1298
1299 if (!start_pcap.tv_sec) {
1300 start_pcap = header->ts;
1301 }
1302
1303 if (RIDEBUG_ENABLED()) {
1304 rs_time_print(timestr, sizeof(timestr), &header->ts);
1305 }
1306
1307 len = fr_pcap_link_layer_offset(data, header->caplen, event->in->link_layer);
1308 if (len < 0) {
1309 REDEBUG("Failed determining link layer header offset");
1310 return;
1311 }
1312 p += len;
1313
1314 version = (p[0] & 0xf0) >> 4;
1315 switch (version) {
1316 case 4:
1317 ip = (ip_header_t const *)p;
1318 len = (0x0f & ip->ip_vhl) * 4; /* ip_hl specifies length in 32bit words */
1319 p += len;
1320 break;
1321
1322 case 6:
1323 ip6 = (ip_header6_t const *)p;
1324 p += sizeof(ip_header6_t);
1325
1326 break;
1327
1328 default:
1329 REDEBUG("IP version invalid %i", version);
1330 return;
1331 }
1332
1333 /*
1334 * End of variable length bits, do basic check now to see if packet looks long enough
1335 */
1336 len = (p - data) + sizeof(udp_header_t) + sizeof(radius_packet_t); /* length value */
1337 if ((size_t) len > header->caplen) {
1338 REDEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
1339 (size_t) len, header->caplen);
1340 return;
1341 }
1342
1343 /*
1344 * UDP header validation.
1345 */
1346 udp = (udp_header_t const *)p;
1347 {
1348 uint16_t udp_len;
1349 ssize_t actual_len;
1350
1351 udp_len = ntohs(udp->len);
1352 actual_len = header->caplen - (p - data);
1353 /* Truncated data */
1354 if (udp_len > actual_len) {
1355 REDEBUG("Packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
1356 udp_len - actual_len, udp_len);
1357 return;
1358 }
1359
1360#if 0
1361 /*
1362 * It seems many probes add trailing garbage to the end
1363 * of each capture frame. This has been observed with
1364 * F5 load balancers and Netscout.
1365 *
1366 * Leaving the code here in case it's ever needed for
1367 * debugging.
1368 */
1369 else if (udp_len < actual_len) {
1370 REDEBUG("Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
1371 actual_len - udp_len, udp_len);
1372 return;
1373 }
1374#endif
1375 if ((version == 4) && conf->verify_udp_checksum) {
1376 uint16_t expected;
1377
1378 expected = fr_udp_checksum((uint8_t const *) udp, udp_len, udp->checksum,
1379 ip->ip_src, ip->ip_dst);
1380 if (udp->checksum != expected) {
1381 REDEBUG("UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
1382 ntohs(udp->checksum), ntohs(expected));
1383 /* Not a fatal error */
1384 }
1385 }
1386 }
1387 p += sizeof(udp_header_t);
1388
1389 /*
1390 * With artificial talloc memory limits there's a good chance we can
1391 * recover once some requests timeout, so make an effort to deal
1392 * with allocation failures gracefully.
1393 */
1394 packet = fr_packet_alloc(conf, false);
1395 if (!packet) {
1396 REDEBUG("Failed allocating memory to hold decoded packet");
1397 rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
1398 return;
1399 }
1400
1401 packet->timestamp = fr_time_from_timeval(&header->ts);
1402 packet->data_len = header->caplen - (p - data);
1403 memcpy(&packet->data, &p, sizeof(packet->data));
1404
1405 packet->socket.type = SOCK_DGRAM;
1406
1407 /*
1408 * Populate IP/UDP fields from PCAP data
1409 */
1410 if (ip) {
1411 packet->socket.inet.src_ipaddr.af = AF_INET;
1412 packet->socket.inet.src_ipaddr.addr.v4.s_addr = ip->ip_src.s_addr;
1413
1414 packet->socket.inet.dst_ipaddr.af = AF_INET;
1415 packet->socket.inet.dst_ipaddr.addr.v4.s_addr = ip->ip_dst.s_addr;
1416 } else {
1417 packet->socket.inet.src_ipaddr.af = AF_INET6;
1418 memcpy(packet->socket.inet.src_ipaddr.addr.v6.s6_addr, ip6->ip_src.s6_addr,
1419 sizeof(packet->socket.inet.src_ipaddr.addr.v6.s6_addr));
1420
1421 packet->socket.inet.dst_ipaddr.af = AF_INET6;
1422 memcpy(packet->socket.inet.dst_ipaddr.addr.v6.s6_addr, ip6->ip_dst.s6_addr,
1423 sizeof(packet->socket.inet.dst_ipaddr.addr.v6.s6_addr));
1424 }
1425
1426 packet->socket.inet.src_port = ntohs(udp->src);
1427 packet->socket.inet.dst_port = ntohs(udp->dst);
1428
1429 if (!fr_packet_ok(packet, RADIUS_MAX_ATTRIBUTES, false, &reason)) {
1430 fr_perror("radsniff");
1431 if (conf->event_flags & RS_ERROR) {
1432 rs_packet_print(NULL, count, RS_ERROR, event->in,
1433 packet, &decoded, &elapsed, NULL, false, false);
1434 }
1435 fr_packet_free(&packet);
1436
1437 return;
1438 }
1439
1440 switch (packet->code) {
1449 {
1450 /* look for a matching request and use it for decoding */
1451 search.expect = packet;
1452 original = fr_rb_find(request_tree, &search);
1453
1454 /*
1455 * Verify this code is allowed
1456 */
1457 if (conf->filter_response_code && (conf->filter_response_code != packet->code)) {
1458 drop_response:
1459 RDEBUG2("Response dropped by filter");
1460 fr_packet_free(&packet);
1461
1462 /* We now need to cleanup the original request too */
1463 if (original) {
1464 RS_CLEANUP_NOW(original, true);
1465 }
1466 return;
1467 }
1468
1469 if (conf->verify_radius_authenticator && original) {
1470 int ret;
1471 FILE *log_fp = fr_log_fp;
1472
1473 fr_log_fp = NULL;
1474 ret = fr_packet_verify(packet, original->expect, conf->radius_secret);
1475 fr_log_fp = log_fp;
1476 if (ret != 0) {
1477 fr_perror("Failed verifying packet ID %d", packet->id);
1478 fr_packet_free(&packet);
1479 return;
1480 }
1481 }
1482
1483 /*
1484 * Only decode attributes if we want to print them or filter on them
1485 * fr_packet_ok( does checks to verify the packet is actually valid.
1486 */
1487 if (conf->decode_attrs) {
1488 int ret;
1489
1490#ifndef NDEBUG
1492#endif
1493
1494 ret = fr_radius_decode_simple(packet, &decoded,
1495 packet->data, packet->data_len,
1496 (original && original->expect && original->expect->data) ?
1497 original->expect->data + 4 : zeros,
1499 if (ret < 0) {
1500 fr_packet_free(&packet); /* Also frees vps */
1501 REDEBUG("Failed decoding");
1502 return;
1503 }
1504 }
1505
1506 /*
1507 * Check if we've managed to link it to a request
1508 */
1509 if (original) {
1510 /*
1511 * Now verify the packet passes the attribute filter
1512 */
1515 if (!fr_pair_validate_relaxed(NULL, &conf->filter_response_vps, &decoded)) {
1516 goto drop_response;
1517 }
1518 }
1519
1520 /*
1521 * Is this a retransmission?
1522 */
1523 if (original->linked) {
1524 status = RS_RTX;
1525 original->rt_rsp++;
1526
1527 /*
1528 * Explicitly free VPs first so list maintains integrity - it is reused below
1529 */
1530 fr_pair_list_free(&original->link_vps);
1531 fr_packet_free(&original->linked);
1532 fr_timer_delete(&original->event);
1533 /*
1534 * ...nope it's the first response to a request.
1535 */
1536 } else {
1537 original->stats_rsp = &stats->exchange[packet->code];
1538 }
1539
1540 /*
1541 * Insert a callback to remove the request and response
1542 * from the tree after the timeout period.
1543 * The delay is so we can detect retransmissions.
1544 */
1545 original->linked = talloc_steal(original, packet);
1546 fr_pair_list_append(&original->link_vps, &decoded); /* Move the vps over */
1547 rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
1548 if (fr_timer_at(original, event->list->tl, &original->event,
1549 fr_time_from_timeval(&original->when),
1550 false, _rs_event, original) < 0) {
1551 REDEBUG("Failed inserting new event");
1552 /*
1553 * Delete the original request/event, it's no longer valid
1554 * for statistics.
1555 */
1556 talloc_free(original);
1557 return;
1558 }
1559 /*
1560 * No request seen, or request was dropped by attribute filter
1561 */
1562 } else {
1563 /*
1564 * If conf->filter_request_vps are set assume the original request was dropped,
1565 * the alternative is maintaining another 'filter', but that adds
1566 * complexity, reduces max capture rate, and is generally a PITA.
1567 */
1568 if (conf->filter_request) {
1569 fr_packet_free(&packet);
1570 RDEBUG2("Original request dropped by filter");
1571 return;
1572 }
1573
1574 status = RS_UNLINKED;
1575 stats->exchange[packet->code].interval.unlinked_total++;
1576 }
1577
1578 rs_response_to_pcap(event, original, header, data);
1579 response = true;
1580 break;
1581 }
1582
1588 {
1589 /*
1590 * Verify this code is allowed
1591 */
1592 if (conf->filter_request_code && (conf->filter_request_code != packet->code)) {
1593 drop_request:
1594
1595 RDEBUG2("Request dropped by filter");
1596 fr_packet_free(&packet);
1597
1598 return;
1599 }
1600
1602 switch (packet->code) {
1603 case FR_RADIUS_CODE_ACCESS_REQUEST: /* Even though this is just random bytes, we still might need to check Message-Authenticator */
1607 {
1608 int ret;
1609 FILE *log_fp = fr_log_fp;
1610
1611 fr_log_fp = NULL;
1612 ret = fr_packet_verify(packet, NULL, conf->radius_secret);
1613 fr_log_fp = log_fp;
1614 if (ret != 0) {
1615 fr_perror("Failed verifying packet ID %d", packet->id);
1616 fr_packet_free(&packet);
1617 return;
1618 }
1619 }
1620 break;
1621
1622 default:
1623 break;
1624 }
1625 }
1626
1627 /*
1628 * Only decode attributes if we want to print them or filter on them
1629 * fr_packet_ok( does checks to verify the packet is actually valid.
1630 */
1631 if (conf->decode_attrs) {
1632 int ret;
1633 FILE *log_fp = fr_log_fp;
1634
1635 fr_log_fp = NULL;
1636 ret = fr_radius_decode_simple(packet, &decoded,
1637 packet->data, packet->data_len, NULL,
1639 fr_log_fp = log_fp;
1640
1641 if (ret < 0) {
1642 fr_packet_free(&packet); /* Also frees vps */
1643
1644 REDEBUG("Failed decoding");
1645 return;
1646 }
1647
1649 }
1650
1651 /*
1652 * Save the request for later matching
1653 */
1654 search.expect = fr_packet_alloc_reply(packet, packet);
1655 if (!search.expect) {
1656 REDEBUG("Failed allocating memory to hold expected reply");
1657 rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
1658 fr_packet_free(&packet);
1659
1660 return;
1661 }
1662 search.expect->code = packet->code;
1663 memcpy(search.expect->vector, packet->vector, sizeof(search.expect->vector));
1664
1665 if ((conf->link_da_num > 0) && (!fr_pair_list_empty(&decoded))) {
1666 int ret;
1667 ret = rs_get_pairs(packet, &search.link_vps, &decoded, conf->link_da,
1668 conf->link_da_num);
1669 if (ret < 0) {
1670 ERROR("Failed extracting RTX linking pairs from request");
1671 fr_packet_free(&packet);
1672 return;
1673 }
1674 }
1675
1676 /*
1677 * If we have linking attributes set, attempt to find a request in the linking tree.
1678 */
1679 if (!fr_pair_list_empty(&search.link_vps)) {
1680 rs_request_t *tuple;
1681
1682 original = fr_rb_find(link_tree, &search);
1683 tuple = fr_rb_find(request_tree, &search);
1684
1685 /*
1686 * If the packet we matched using attributes is not the same
1687 * as the packet in the request tree, then we need to clean up
1688 * the packet in the request tree.
1689 */
1690 if (tuple && (original != tuple)) {
1691 RS_CLEANUP_NOW(tuple, true);
1692 }
1693 /*
1694 * Detect duplicates using the normal 5-tuple of src/dst ips/ports id
1695 */
1696 } else {
1697 original = fr_rb_find(request_tree, &search);
1698 if (original && (memcmp(original->expect->vector, packet->vector,
1699 sizeof(original->expect->vector)) != 0)) {
1700 /*
1701 * ID reused before the request timed out (which may be an issue)...
1702 */
1703 if (!original->linked) {
1704 status = RS_REUSED;
1705 stats->exchange[packet->code].interval.reused_total++;
1706 /* Occurs regularly downstream of proxy servers (so don't complain) */
1707 RS_CLEANUP_NOW(original, true);
1708 /*
1709 * ...and before we saw a response (which may be a bigger issue).
1710 */
1711 } else {
1712 RS_CLEANUP_NOW(original, false);
1713 }
1714 /* else it's a proper RTX with the same src/dst id authenticator/nonce */
1715 }
1716 }
1717
1718 /*
1719 * Now verify the packet passes the attribute filter
1720 */
1722 if (!fr_pair_validate_relaxed(NULL, &conf->filter_request_vps, &decoded)) {
1723 goto drop_request;
1724 }
1725 }
1726
1727 /*
1728 * Is this a retransmission?
1729 */
1730 if (original) {
1731 status = RS_RTX;
1732 original->rt_req++;
1733
1734
1735 /* We may of seen the response, but it may of been lost upstream */
1736 fr_pair_list_free(&original->link_vps);
1737 fr_packet_free(&original->linked);
1738
1739 /* replace packet and vps */
1740 fr_pair_list_free(&original->packet_vps);
1741 fr_packet_free(&original->packet);
1742 original->packet = talloc_steal(original, packet);
1743 fr_pair_list_append(&original->packet_vps, &decoded);
1744
1745 /* Request may need to be reinserted as the 5 tuple of the response may of changed */
1746 if (rs_packet_cmp(original, &search) != 0) {
1747 fr_rb_delete(request_tree, original);
1748 }
1749
1750 /* replace expected packets and vps */
1751 fr_pair_list_free(&original->expect_vps);
1752 fr_packet_free(&original->expect);
1753 original->expect = talloc_steal(original, search.expect);
1754 fr_pair_list_append(&original->expect_vps, &search.expect_vps);
1755
1756 /* Disarm the timer for the cleanup event for the original request */
1757 fr_timer_delete(&original->event);
1758 /*
1759 * ...nope it's a new request.
1760 */
1761 } else {
1762 original = rs_request_alloc(conf);
1763 original->id = count;
1764 original->in = event->in;
1765 original->stats_req = &stats->exchange[packet->code];
1766
1767 /* Set the packet pointer to the start of the buffer*/
1768 original->capture_p = original->capture;
1769
1770 original->packet = talloc_steal(original, packet);
1771 fr_pair_list_append(&original->packet_vps, &decoded);
1772
1773 original->expect = talloc_steal(original, search.expect);
1774 fr_pair_list_append(&original->expect_vps, &search.expect_vps);
1775
1776 if (!fr_pair_list_empty(&search.link_vps)) {
1777 bool ret;
1778 fr_pair_t *vp;
1779
1780 for (vp = fr_pair_list_head(&search.link_vps);
1781 vp;
1782 vp = fr_pair_list_next(&search.link_vps, vp)) {
1783 fr_pair_steal(original, vp);
1784 }
1785 fr_pair_list_append(&original->link_vps, &search.link_vps);
1786
1787 /* We should never have conflicts */
1788 ret = fr_rb_insert(link_tree, original);
1789 RS_ASSERT(ret);
1790 original->in_link_tree = true;
1791 }
1792
1793 /*
1794 * Special case for when were filtering by response,
1795 * we never count any requests as lost, because we
1796 * don't know what the response to that request would
1797 * of been.
1798 */
1800 original->silent_cleanup = true;
1801 }
1802 }
1803
1804 if (!original->in_request_tree) {
1805 bool ret;
1806
1807 /* We should never have conflicts */
1808 ret = fr_rb_insert(request_tree, original);
1809 RS_ASSERT(ret);
1810 original->in_request_tree = true;
1811 }
1812
1813 /*
1814 * Insert a callback to remove the request from the tree
1815 */
1816 original->packet->timestamp = fr_time_from_timeval(&header->ts);
1817 rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
1818 if (fr_timer_at(original, event->list->tl, &original->event,
1819 fr_time_from_timeval(&original->when),
1820 false, _rs_event, original) < 0) {
1821 REDEBUG("Failed inserting new event");
1822
1823 talloc_free(original);
1824 return;
1825 }
1826 rs_request_to_pcap(event, original, header, data);
1827 response = false;
1828 break;
1829 }
1830
1831 default:
1832 REDEBUG("Unsupported code %i", packet->code);
1833 fr_packet_free(&packet);
1834
1835 return;
1836 }
1837
1838 fr_timeval_subtract(&elapsed, &header->ts, &start_pcap);
1839
1840 /*
1841 * Increase received count
1842 */
1843 stats->exchange[packet->code].interval.received_total++;
1844
1845 /*
1846 * It's a linked response
1847 */
1848 if (original && original->linked) {
1849 latency = fr_time_delta_to_timeval(fr_time_sub(packet->timestamp, original->packet->timestamp));
1850
1851 /*
1852 * Update stats for both the request and response types.
1853 *
1854 * This isn't useful for things like Access-Requests, but will be useful for
1855 * CoA and Disconnect Messages, as we get the average latency across both
1856 * response types.
1857 *
1858 * It also justifies allocating FR_RADIUS_CODE_MAXinstances of rs_latency_t.
1859 */
1860 rs_stats_update_latency(&stats->exchange[packet->code], &latency);
1861 if (original->expect) rs_stats_update_latency(&stats->exchange[original->expect->code], &latency);
1862
1863 /*
1864 * We're filtering on response, now print out the full data from the request
1865 */
1867 struct timeval ts_tv;
1868
1869 ts_tv = fr_time_to_timeval(original->packet->timestamp);
1870
1871 rs_time_print(timestr, sizeof(timestr), &ts_tv);
1872 fr_timeval_subtract(&elapsed, &ts_tv, &start_pcap);
1873 rs_packet_print(original, original->id, RS_NORMAL, original->in,
1874 original->packet, &original->packet_vps, &elapsed, NULL, false, true);
1875 fr_timeval_subtract(&elapsed, &header->ts, &start_pcap);
1876 rs_time_print(timestr, sizeof(timestr), &header->ts);
1877 }
1878
1879 if (conf->event_flags & status) {
1880 rs_packet_print(original, count, status, event->in,
1881 original->linked, &original->link_vps,
1882 &elapsed, &latency, response, true);
1883 }
1884 /*
1885 * It's the original request
1886 *
1887 * If we're filtering on responses we can only indicate we received it on response, or timeout.
1888 */
1889 } else if (!conf->filter_response && (conf->event_flags & status)) {
1890 uint64_t print_id;
1892 fr_pair_list_t *print_pair_list;
1893
1894 if (original) {
1895 print_id = original->id;
1896 print_packet = original->packet;
1897 print_pair_list = &original->packet_vps;
1898 } else {
1899 print_id = count;
1900 print_packet = packet;
1901 print_pair_list = &decoded;
1902 }
1903
1904 rs_packet_print(original, print_id, status, event->in,
1905 print_packet, print_pair_list,
1906 &elapsed, NULL, response, true);
1907 }
1908
1909 fflush(fr_log_fp);
1910
1911 /*
1912 * If it's an unlinked response, we need to free it explicitly, as it will
1913 * not be done by the event queue.
1914 */
1915 if (response && !original) {
1916 fr_packet_free(&packet); /* Also frees decoded */
1917 }
1918
1919 captured++;
1920 /*
1921 * We've hit our capture limit, break out of the event loop
1922 */
1923 if ((conf->limit > 0) && (captured >= conf->limit)) {
1924 INFO("Captured %" PRIu64 " packets, exiting...", captured);
1926 }
1927}
1928
1929static void rs_got_packet(fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
1930{
1931 static uint64_t count = 0; /* Packets seen */
1932 static fr_time_t last_sync = fr_time_wrap(0);
1933 fr_time_t now_real;
1934 rs_event_t *event = talloc_get_type(ctx, rs_event_t);
1935 pcap_t *handle = event->in->handle;
1936
1937 int i;
1938 int ret;
1939 const uint8_t *data;
1940 struct pcap_pkthdr *header;
1941
1942 /*
1943 * Because the event loop might be running on synthetic
1944 * pcap file time, we need to implement our own time
1945 * tracking here, and run the monotonic/wallclock sync
1946 * event ourselves.
1947 */
1948 now_real = fr_time();
1949 if (fr_time_delta_gt(fr_time_sub(now_real, last_sync), fr_time_delta_from_sec(1))) {
1950 fr_time_sync();
1951 last_sync = now_real;
1952 }
1953
1954 /*
1955 * Consume entire capture, interleaving not currently possible
1956 */
1957 if ((event->in->type == PCAP_FILE_IN) || (event->in->type == PCAP_STDIO_IN)) {
1958 bool stats_started = false;
1959
1960 while (!fr_event_loop_exiting(el)) {
1961 fr_time_t now;
1962
1963 ret = pcap_next_ex(handle, &header, &data);
1964 if (ret == 0) {
1965 /* No more packets available at this time */
1966 return;
1967 }
1968 if (ret == -2) {
1969 DEBUG("Done reading packets (%s)", event->in->name);
1970 done_file:
1972
1973 /* Signal pipe takes one slot which is why this is == 1 */
1975
1976 return;
1977 }
1978 if (ret < 0) {
1979 ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
1980 goto done_file;
1981 }
1982
1983 /*
1984 * Insert the stats processor with the timestamp
1985 * of the first packet in the trace.
1986 */
1987 if (conf->stats.interval && !stats_started) {
1988 rs_install_stats_processor(event->stats, el, NULL, &header->ts, false);
1989 stats_started = true;
1990 }
1991
1992 do {
1993 now = fr_time_from_timeval(&header->ts);
1994 } while (fr_timer_list_run(el->tl, &now) == 1);
1995 count++;
1996
1997 rs_packet_process(count, event, header, data);
1998 }
1999 return;
2000 }
2001
2002 /*
2003 * Consume multiple packets from the capture buffer.
2004 * We occasionally need to yield to allow events to run.
2005 */
2006 for (i = 0; i < RS_FORCE_YIELD; i++) {
2007 ret = pcap_next_ex(handle, &header, &data);
2008 if (ret == 0) {
2009 /* No more packets available at this time */
2010 return;
2011 }
2012 if (ret < 0) {
2013 ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
2014 return;
2015 }
2016
2017 count++;
2018 rs_packet_process(count, event, header, data);
2019 }
2020}
2021
2022static int _rs_event_status(UNUSED fr_time_t now, fr_time_delta_t wake_t, UNUSED void *uctx)
2023{
2024 struct timeval wake;
2025
2026 wake = fr_time_delta_to_timeval(wake_t);
2027
2028 if ((wake.tv_sec != 0) || (wake.tv_usec >= 100000)) {
2029 DEBUG2("Waking up in %d.%01u seconds", (int) wake.tv_sec, (unsigned int) wake.tv_usec / 100000);
2030
2031 if (RIDEBUG_ENABLED()) {
2032 rs_time_print(timestr, sizeof(timestr), &wake);
2033 }
2034 }
2035
2036 return 0;
2037}
2038
2039/** Compare requests using packet info and lists of attributes
2040 *
2041 */
2042static int8_t rs_rtx_cmp(void const *one, void const *two)
2043{
2044 rs_request_t const *a = one;
2045 rs_request_t const *b = two;
2046 int ret;
2047
2050
2051 CMP_RETURN(a, b, expect->code);
2052 CMP_RETURN(a, b, expect->socket.fd);
2053
2054 ret = fr_ipaddr_cmp(&a->expect->socket.inet.src_ipaddr, &b->expect->socket.inet.src_ipaddr);
2055 if (ret != 0) return ret;
2056
2057 ret = fr_ipaddr_cmp(&a->expect->socket.inet.dst_ipaddr, &b->expect->socket.inet.dst_ipaddr);
2058 if (ret != 0) return ret;
2059
2060 ret = fr_pair_list_cmp(&a->link_vps, &b->link_vps);
2061 return CMP(ret, 0);
2062}
2063
2064static int rs_build_dict_list(fr_dict_attr_t const **out, size_t len, char *list)
2065{
2066 size_t i = 0;
2067 char *p, *tok;
2068
2069 p = list;
2070 while ((tok = strsep(&p, "\t ,")) != NULL) {
2071 fr_dict_attr_t const *da;
2072 if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
2073 continue;
2074 }
2075
2076 if (i == len) {
2077 ERROR("Too many attributes, maximum allowed is %zu", len);
2078 return -1;
2079 }
2080
2082 if (!da) da = fr_dict_attr_by_name(NULL, fr_dict_root(dict_freeradius), tok);
2083 if (!da) {
2084 ERROR("Error parsing attribute name \"%s\"", tok);
2085 return -1;
2086 }
2087
2088 out[i] = da;
2089 i++;
2090 }
2091
2092 /*
2093 * This allows efficient list comparisons later
2094 */
2095 if (i > 1) fr_quick_sort((void const **)out, 0, i, fr_pointer_cmp);
2096
2097 return i;
2098}
2099
2100static int rs_build_filter(fr_pair_list_t *out, char const *filter)
2101{
2102 fr_pair_parse_t root, relative;
2103
2104 root = (fr_pair_parse_t) {
2105 .ctx = conf,
2107 .list = out,
2108 .allow_compare = true,
2109 };
2110 relative = (fr_pair_parse_t) { };
2111
2112 if (fr_pair_list_afrom_substr(&root, &relative, &FR_SBUFF_IN(filter, strlen(filter))) <= 0) {
2113 fr_perror("Invalid RADIUS filter \"%s\"", filter);
2114 return -1;
2115 }
2116
2117 if (fr_pair_list_empty(out)) {
2118 ERROR("Empty RADIUS filter '%s'", filter);
2119 return -1;
2120 }
2121
2122 /*
2123 * This allows efficient list comparisons later
2124 */
2126
2127 return 0;
2128}
2129
2130static int rs_build_event_flags(int *flags, fr_table_num_sorted_t const *map, size_t map_len, char *list)
2131{
2132 size_t i = 0;
2133 char *p, *tok;
2134
2135 p = list;
2136 while ((tok = strsep(&p, "\t ,")) != NULL) {
2137 int flag;
2138
2139 if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
2140 continue;
2141 }
2142
2143 *flags |= flag = fr_table_value_by_str(map, tok, -1);
2144 if (flag < 0) {
2145 ERROR("Invalid flag \"%s\"", tok);
2146 return -1;
2147 }
2148
2149 i++;
2150 }
2151
2152 return i;
2153}
2154
2155/** Callback for when the request is removed from the request tree
2156 *
2157 * @param request being removed.
2158 */
2159static void _unmark_request(void *request)
2160{
2161 rs_request_t *this = request;
2162 this->in_request_tree = false;
2163}
2164
2165/** Callback for when the request is removed from the link tree
2166 *
2167 * @param request being removed.
2168 */
2169static void _unmark_link(void *request)
2170{
2171 rs_request_t *this = request;
2172 this->in_link_tree = false;
2173}
2174
2175/** Exit the event loop after a given timeout.
2176 *
2177 */
2178static void timeout_event(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now_t, void *ctx)
2179{
2180 fr_event_loop_exit(talloc_get_type_abort(ctx, fr_event_list_t), 1);
2181}
2182
2183
2184#ifdef HAVE_COLLECTDC_H
2185/** Re-open the collectd socket
2186 *
2187 */
2188static void rs_collectd_reopen(fr_timer_list_t *tl, fr_time_t now, UNUSED void *ctx)
2189{
2190 static fr_timer_t *event;
2191
2192 if (rs_stats_collectd_open(conf) == 0) {
2193 DEBUG2("Stats output socket (re)opened");
2194 return;
2195 }
2196
2197 ERROR("Will attempt to re-establish connection in %i ms", RS_SOCKET_REOPEN_DELAY);
2198
2199 if (fr_timer_at(NULL, tl, &event,
2201 false, rs_collectd_reopen, NULL) < 0) {
2202 ERROR("Failed inserting re-open event");
2203 RS_ASSERT(0);
2204 }
2205}
2206#endif
2207
2208/** Write the last signal to the signal pipe
2209 *
2210 * @param sig raised
2211 */
2212static void rs_signal_self(int sig)
2213{
2214 if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
2215 ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
2216 fr_exit_now(EXIT_FAILURE);
2217 }
2218}
2219
2220/** Read the last signal from the signal pipe
2221 *
2222 */
2224#ifndef HAVE_COLLECTDC_H
2225UNUSED
2226#endif
2227fr_event_list_t *list, int fd, int UNUSED flags, UNUSED void *ctx)
2228{
2229 int sig;
2230 ssize_t ret;
2231
2232 ret = read(fd, &sig, sizeof(sig));
2233 if (ret < 0) {
2234 ERROR("Failed reading signal from pipe: %s", fr_syserror(errno));
2235 fr_exit_now(EXIT_FAILURE);
2236 }
2237
2238 if (ret != sizeof(sig)) {
2239 ERROR("Failed reading signal from pipe: "
2240 "Expected signal to be %zu bytes but only read %zu byes", sizeof(sig), ret);
2241 fr_exit_now(EXIT_FAILURE);
2242 }
2243
2244 switch (sig) {
2245#ifdef HAVE_COLLECTDC_H
2246 case SIGPIPE:
2247 rs_collectd_reopen(list->tl, fr_time(), list);
2248 break;
2249#else
2250 case SIGPIPE:
2251#endif
2252
2253 case SIGINT:
2254 case SIGTERM:
2255 case SIGQUIT:
2256 DEBUG2("Signalling event loop to exit");
2258 break;
2259
2260 default:
2261 ERROR("Unhandled signal %s", strsignal(sig));
2262 fr_exit_now(EXIT_FAILURE);
2263 }
2264}
2265
2266static NEVER_RETURNS void usage(int status)
2267{
2268 FILE *output = status ? stderr : stdout;
2269 fprintf(output, "Usage: radsniff [options][stats options] -- [pcap files]\n");
2270 fprintf(output, "options:\n");
2271 fprintf(output, " -a List all interfaces available for capture.\n");
2272 fprintf(output, " -c <count> Number of packets to capture.\n");
2273 fprintf(output, " -C <checksum_type> Enable checksum validation. (Specify 'udp' or 'radius')\n");
2274 fprintf(output, " -d <raddb> Set configuration directory (defaults to " RADDBDIR ").\n");
2275 fprintf(output, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
2276 fprintf(output, " -e <event>[,<event>] Only log requests with these event flags.\n");
2277 fprintf(output, " Event may be one of the following:\n");
2278 fprintf(output, " - received - a request or response.\n");
2279 fprintf(output, " - norsp - seen for a request.\n");
2280 fprintf(output, " - rtx - of a request that we've seen before.\n");
2281 fprintf(output, " - noreq - could be matched with the response.\n");
2282 fprintf(output, " - reused - ID too soon.\n");
2283 fprintf(output, " - error - decoding the packet.\n");
2284 fprintf(output, " -f <filter> PCAP filter (default is 'udp port <port> or <port + 1> or %i'\n",
2286 fprintf(output, " with extra rules to allow .1Q tagged packets)\n");
2287 fprintf(output, " -h This help message.\n");
2288 fprintf(output, " -i <interface> Capture packets from interface (defaults to all if supported).\n");
2289 fprintf(output, " -I <file> Read packets from <file>\n");
2290 fprintf(output, " -l <attr>[,<attr>] Output packet sig and a list of attributes.\n");
2291 fprintf(output, " -L <attr>[,<attr>] Detect retransmissions using these attributes to link requests.\n");
2292 fprintf(output, " -m Don't put interface(s) into promiscuous mode.\n");
2293 fprintf(output, " -p <port> Filter packets by port (default is %i).\n", FR_AUTH_UDP_PORT);
2294 fprintf(output, " -P <pidfile> Daemonize and write out <pidfile>.\n");
2295 fprintf(output, " -q Print less debugging information.\n");
2296 fprintf(output, " -r <filter> RADIUS attribute request filter.\n");
2297 fprintf(output, " -R <filter> RADIUS attribute response filter.\n");
2298 fprintf(output, " -s <secret> RADIUS secret.\n");
2299 fprintf(output, " -S Write PCAP data to stdout.\n");
2300 fprintf(output, " -t <timeout> Stop after <timeout> seconds.\n");
2301 fprintf(output, " -v Show program version information and exit.\n");
2302 fprintf(output, " -w <file> Write output packets to file.\n");
2303 fprintf(output, " -x Print more debugging information.\n");
2304 fprintf(output, "stats options:\n");
2305 fprintf(output, " -W <interval> Periodically write out statistics every <interval> seconds.\n");
2306 fprintf(output, " -E Print stats in CSV format.\n");
2307 fprintf(output, " -T <timeout> How many milliseconds before the request is counted as lost "
2308 "(defaults to %i).\n", RS_DEFAULT_TIMEOUT);
2309#ifdef HAVE_COLLECTDC_H
2310 fprintf(output, " -N <prefix> The instance name passed to the collectd plugin.\n");
2311 fprintf(output, " -O <server> Write statistics to this collectd server.\n");
2312#endif
2313 fprintf(output, " -Z <output_dir> Dump the packets in <output_dir>/{requests,reply}.${count}.txt\n");
2314 fr_exit_now(status);
2315}
2316
2317/**
2318 *
2319 * @hidecallgraph
2320 */
2321int main(int argc, char *argv[])
2322{
2323 fr_pcap_t *in = NULL, *in_p;
2324 fr_pcap_t **in_head = &in;
2325 fr_pcap_t *out = NULL;
2326
2327 int ret = EXIT_SUCCESS; /* Exit status */
2328
2329 char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */
2330 int port = FR_AUTH_UDP_PORT;
2331
2332 int c;
2333 unsigned int timeout = 0;
2334 fr_timer_t *timeout_ev = NULL;
2335 char const *raddb_dir = RADDBDIR;
2336 char const *dict_dir = DICTDIR;
2337 TALLOC_CTX *autofree;
2338
2339 rs_stats_t *stats;
2340
2341 fr_debug_lvl = 1;
2342 fr_log_fp = stdout;
2343
2344 /*
2345 * Must be called first, so the handler is called last
2346 */
2348
2350
2351 /*
2352 * Useful if using radsniff as a long running stats daemon
2353 */
2354#ifndef NDEBUG
2355 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
2356 fr_perror("radsniff");
2357 fr_exit_now(EXIT_FAILURE);
2358 }
2359#endif
2360
2361 talloc_set_log_stderr();
2362
2363 conf = talloc_zero(autofree, rs_t);
2364 RS_ASSERT(conf);
2367
2368 stats = talloc_zero(conf, rs_stats_t);
2369
2370 /*
2371 * Set some defaults
2372 */
2373 conf->print_packet = true;
2374 conf->limit = 0;
2375 conf->promiscuous = true;
2376#ifdef HAVE_COLLECTDC_H
2377 conf->stats.prefix = RS_DEFAULT_PREFIX;
2378#endif
2379 conf->radius_secret = talloc_strdup(conf, RS_DEFAULT_SECRET);
2380 conf->logger = NULL;
2381
2382#ifdef HAVE_COLLECTDC_H
2383 conf->stats.prefix = RS_DEFAULT_PREFIX;
2384#endif
2385
2386 /*
2387 * Get options
2388 */
2389 while ((c = getopt(argc, argv, "ab:c:C:d:D:e:Ef:hi:I:l:L:mp:P:qr:R:s:St:vw:xXW:T:P:N:O:Z:")) != -1) {
2390 switch (c) {
2391 case 'a':
2392 {
2393 pcap_if_t *all_devices = NULL;
2394 pcap_if_t *dev_p;
2395 int i;
2396
2397 if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2398 ERROR("Error getting available capture devices: %s", errbuf);
2399 goto finish;
2400 }
2401
2402 i = 1;
2403 for (dev_p = all_devices;
2404 dev_p;
2405 dev_p = dev_p->next) {
2406 INFO("%i.%s", i++, dev_p->name);
2407 }
2408 ret = 0;
2409 pcap_freealldevs(all_devices);
2410 goto finish;
2411 }
2412
2413 /* super secret option */
2414 case 'b':
2415 conf->buffer_pkts = atoi(optarg);
2416 if (conf->buffer_pkts == 0) {
2417 ERROR("Invalid buffer length \"%s\"", optarg);
2418 usage(1);
2419 }
2420 break;
2421
2422 case 'c':
2423 conf->limit = atoi(optarg);
2424 if (conf->limit == 0) {
2425 ERROR("Invalid number of packets \"%s\"", optarg);
2426 usage(1);
2427 }
2428 break;
2429
2430 /* UDP/RADIUS checksum validation */
2431 case 'C':
2432 if (strcmp(optarg, "udp") == 0) {
2433 conf->verify_udp_checksum = true;
2434
2435 } else if (strcmp(optarg, "radius") == 0) {
2437
2438 } else {
2439 ERROR("Must specify 'udp' or 'radius' for -C, not %s", optarg);
2440 usage(1);
2441 }
2442 break;
2443
2444 case 'd':
2445 raddb_dir = optarg;
2446 break;
2447
2448 case 'D':
2449 dict_dir = optarg;
2450 break;
2451
2452 case 'e':
2454 rs_events, rs_events_len, optarg) < 0) usage(64);
2455 break;
2456
2457 case 'E':
2459 break;
2460
2461 case 'f':
2462 conf->pcap_filter = optarg;
2463 break;
2464
2465 case 'h':
2466 usage(0); /* never returns */
2467
2468 case 'i':
2469 *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
2470 if (!*in_head) goto finish;
2471 in_head = &(*in_head)->next;
2472 conf->from_dev = true;
2473 break;
2474
2475 case 'I':
2476 *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
2477 if (!*in_head) {
2478 goto finish;
2479 }
2480 in_head = &(*in_head)->next;
2481 conf->from_file = true;
2482 break;
2483
2484 case 'l':
2485 conf->list_attributes = optarg;
2486 break;
2487
2488 case 'L':
2489 conf->link_attributes = optarg;
2490 break;
2491
2492 case 'm':
2493 conf->promiscuous = false;
2494 break;
2495
2496 case 'p':
2497 port = atoi(optarg);
2498 break;
2499
2500 case 'P':
2501 conf->daemonize = true;
2502 conf->pidfile = optarg;
2503 break;
2504
2505 case 'q':
2506 if (fr_debug_lvl > 0) {
2507 fr_debug_lvl--;
2508 }
2509 break;
2510
2511 case 'r':
2512 conf->filter_request = optarg;
2513 break;
2514
2515 case 'R':
2516 conf->filter_response = optarg;
2517 break;
2518
2519 case 's':
2521 conf->radius_secret = talloc_strdup(conf, optarg);
2522 break;
2523
2524 case 't':
2525 timeout = atoi(optarg);
2526 break;
2527
2528 case 'S':
2529 conf->to_stdout = true;
2530 break;
2531
2532 case 'v':
2533#ifdef HAVE_COLLECTDC_H
2534 INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
2535 lcc_version_string());
2536#else
2537 INFO("%s %s", radsniff_version, pcap_lib_version());
2538#endif
2539 fr_exit_now(EXIT_SUCCESS);
2540
2541 case 'w':
2542 out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
2543 if (!out) {
2544 ERROR("Failed creating pcap file \"%s\"", optarg);
2545 fr_exit_now(EXIT_FAILURE);
2546 }
2547 conf->to_file = true;
2548 break;
2549
2550 case 'x':
2551 case 'X':
2552 fr_debug_lvl++;
2553 break;
2554
2555 case 'W':
2556 conf->stats.interval = atoi(optarg);
2557 conf->print_packet = false;
2558 if (conf->stats.interval <= 0) {
2559 ERROR("Stats interval must be > 0");
2560 usage(64);
2561 }
2562 break;
2563
2564 case 'Z':
2565 {
2566 char *p = optarg;
2567 size_t len = strlen(p);
2568
2569 conf->to_output_dir = true;
2570
2571 /* Strip the last '/' */
2572 if (p[len-1] == '/') p[len-1] = '\0';
2573
2574 conf->output_dir = p;
2575
2576 if (fr_mkdir(NULL, conf->output_dir, -1, 0755, NULL, NULL) < 0) {
2577 ERROR("Failed to create directory %s: %s", conf->output_dir, fr_syserror(errno));
2578 usage(64);
2579 }
2580 break;
2581 }
2582
2583 case 'T':
2584 conf->stats.timeout = atoi(optarg);
2585 if (conf->stats.timeout <= 0) {
2586 ERROR("Timeout value must be > 0");
2587 usage(64);
2588 }
2589 break;
2590
2591#ifdef HAVE_COLLECTDC_H
2592 case 'N':
2593 conf->stats.prefix = optarg;
2594 break;
2595
2596 case 'O':
2597 conf->stats.collectd = optarg;
2598 conf->stats.out = RS_STATS_OUT_COLLECTD;
2599 break;
2600#endif
2601 default:
2602 usage(64);
2603 }
2604 }
2605
2606 /*
2607 * Mismatch between the binary and the libraries it depends on
2608 */
2610 fr_perror("radsniff");
2611 fr_exit_now(EXIT_FAILURE);
2612 }
2613
2614 /* Useful for file globbing */
2615 while (optind < argc) {
2616 *in_head = fr_pcap_init(conf, argv[optind], PCAP_FILE_IN);
2617 if (!*in_head) {
2618 goto finish;
2619 }
2620 in_head = &(*in_head)->next;
2621 conf->from_file = true;
2622 optind++;
2623 }
2624
2625 /* Is stdin not a tty? If so it's probably a pipe */
2626 if (!isatty(fileno(stdin))) {
2627 conf->from_stdin = true;
2628 }
2629
2630 /* What's the point in specifying -F ?! */
2631 if (conf->from_stdin && conf->from_file && conf->to_file) {
2632 usage(64);
2633 }
2634
2635 /* Can't read from both... */
2636 if (conf->from_file && conf->from_dev) {
2637 ERROR("Can't read from both a file and a device");
2638 usage(64);
2639 }
2640
2641 /* Can't set stats export mode if we're not writing stats */
2642 if ((conf->stats.out == RS_STATS_OUT_STDIO_CSV) && !conf->stats.interval) {
2643 ERROR("CSV output requires a statistics interval (-W)");
2644 usage(64);
2645 }
2646
2647 /* Reading from file overrides stdin */
2648 if (conf->from_stdin && (conf->from_file || conf->from_dev)) {
2649 conf->from_stdin = false;
2650 }
2651
2652 /* Writing to file overrides stdout */
2653 if (conf->to_file && conf->to_stdout) {
2654 conf->to_stdout = false;
2655 }
2656
2657 if (conf->to_stdout) {
2658 out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT);
2659 if (!out) {
2660 goto finish;
2661 }
2662 }
2663
2664 if (conf->from_stdin) {
2665 *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN);
2666 if (!*in_head) {
2667 goto finish;
2668 }
2669 in_head = &(*in_head)->next;
2670 }
2671
2672 /* Set the default stats output */
2673 if (conf->stats.interval && !conf->stats.out) {
2675 }
2676
2677 if (conf->stats.timeout == 0) {
2678 conf->stats.timeout = RS_DEFAULT_TIMEOUT;
2679 }
2680
2681 /*
2682 * If we're writing pcap data, or CSV to stdout we *really* don't want to send
2683 * logging there as well.
2684 */
2686 fr_log_fp = stderr;
2687 }
2688
2689 if (conf->list_attributes) {
2691 } else if (conf->to_output_dir) {
2693 } else if (fr_debug_lvl > 0) {
2695 }
2696
2697#if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
2698 if (conf->from_stdin || conf->to_stdout) {
2699 ERROR("PCAP streams not supported");
2700 goto finish;
2701 }
2702#endif
2703
2704 if (!conf->pcap_filter) {
2705 conf->pcap_filter = talloc_asprintf(conf, "udp port %d or %d or %d", port, port + 1, FR_COA_UDP_PORT);
2706
2707 /*
2708 * Using the VLAN keyword strips off the .1q tag
2709 * allowing the UDP filter to work. Without this
2710 * tagged packets aren't processed.
2711 */
2712 conf->pcap_filter_vlan = talloc_asprintf(conf, "(%s) or (vlan and (%s))",
2714 }
2715
2716 if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
2717 fr_perror("radsniff");
2718 fr_exit_now(EXIT_FAILURE);
2719 }
2720
2722 fr_perror("radsniff");
2723 ret = 64;
2724 goto finish;
2725 }
2726
2728 fr_perror("radsniff");
2729 ret = 64;
2730 goto finish;
2731 }
2732
2734 fr_perror("radsniff");
2735 ret = 64;
2736 goto finish;
2737 }
2738
2739 /* Initialise the protocol library */
2740 if (fr_radius_global_init() < 0) {
2741 fr_perror("radclient");
2742 return 1;
2743 }
2744
2745 fr_strerror_clear(); /* Clear out any non-fatal errors */
2746
2747 if (conf->list_attributes) {
2750 if (conf->list_da_num < 0) {
2751 usage(64);
2752 }
2754 }
2755
2756 if (conf->link_attributes) {
2759 if (conf->link_da_num < 0) {
2760 usage(64);
2761 }
2762
2764 if (!link_tree) {
2765 ERROR("Failed creating RTX tree");
2766 goto finish;
2767 }
2768 }
2769
2770 if (conf->filter_request) {
2771 fr_dcursor_t cursor;
2772 fr_pair_t *type;
2773
2775
2777 if (type) {
2778 fr_dcursor_remove(&cursor);
2779 conf->filter_request_code = type->vp_uint32;
2781 }
2782 }
2783
2784 if (conf->filter_response) {
2785 fr_dcursor_t cursor;
2786 fr_pair_t *type;
2787
2789
2791 if (type) {
2792 fr_dcursor_remove(&cursor);
2793 conf->filter_response_code = type->vp_uint32;
2795 }
2796 }
2797
2798 /*
2799 * Default to logging and capturing all events
2800 */
2801 if (conf->event_flags == 0) {
2802 DEBUG("Logging all events");
2803 memset(&conf->event_flags, 0xff, sizeof(conf->event_flags));
2804 }
2805
2806 /*
2807 * If we need to list attributes, link requests using attributes, filter attributes
2808 * or print the packet contents, we need to decode the attributes.
2809 *
2810 * But, if were just logging requests, or graphing packets, we don't need to decode
2811 * attributes.
2812 */
2814 conf->print_packet) {
2815 conf->decode_attrs = true;
2816 }
2817
2818 /*
2819 * Setup the request tree
2820 */
2822 if (!request_tree) {
2823 ERROR("Failed creating request tree");
2824 goto finish;
2825 }
2826
2827 /*
2828 * Get the default capture device
2829 */
2830 if (!conf->from_stdin && !conf->from_file && !conf->from_dev) {
2831 pcap_if_t *all_devices; /* List of all devices libpcap can listen on */
2832 pcap_if_t *dev_p;
2833
2834 if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2835 ERROR("Error getting available capture devices: %s", errbuf);
2836 goto finish;
2837 }
2838
2839 if (!all_devices) {
2840 ERROR("No capture files specified and no live interfaces available");
2841 ret = 64;
2842 pcap_freealldevs(all_devices);
2843 goto finish;
2844 }
2845
2846 for (dev_p = all_devices;
2847 dev_p;
2848 dev_p = dev_p->next) {
2849 int link_layer;
2850
2851 /* Don't use the any device, it's horribly broken */
2852 if (!strcmp(dev_p->name, "any")) continue;
2853
2854 /* ...same here. See https://github.com/FreeRADIUS/freeradius-server/pull/3364 for details */
2855 if (!strncmp(dev_p->name, "pktap", 5)) continue;
2856
2857 link_layer = fr_pcap_if_link_layer(dev_p);
2858 if (link_layer < 0) {
2859 DEBUG2("Skipping %s: %s", dev_p->name, fr_strerror());
2860 continue;
2861 }
2862
2863 if (!fr_pcap_link_layer_supported(link_layer)) {
2864 DEBUG2("Skipping %s: datalink type %s not supported",
2865 dev_p->name, pcap_datalink_val_to_name(link_layer));
2866 continue;
2867 }
2868
2869 *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
2870 in_head = &(*in_head)->next;
2871 }
2872 pcap_freealldevs(all_devices);
2873
2874 conf->from_auto = true;
2875 conf->from_dev = true;
2876 INFO("Defaulting to capture on all interfaces");
2877 }
2878
2879 /*
2880 * Print captures values which will be used
2881 */
2882 if (fr_debug_lvl > 2) {
2883 DEBUG2("Sniffing with options:");
2884 if (conf->from_dev) {
2885 char *buff = fr_pcap_device_names(conf, in, ' ');
2886 DEBUG2(" Device(s) : [%s]", buff);
2888 }
2889 if (out) {
2890 DEBUG2(" Writing to : [%s]", out->name);
2891 }
2892 if (conf->limit > 0) {
2893 DEBUG2(" Capture limit (packets) : [%" PRIu64 "]", conf->limit);
2894 }
2895 DEBUG2(" PCAP filter : [%s]", conf->pcap_filter);
2896 DEBUG2(" RADIUS secret : [%s]", conf->radius_secret);
2897
2899 DEBUG2(" RADIUS request code : [%s]", fr_radius_packet_name[conf->filter_request_code]);
2900 }
2901
2903 DEBUG2(" RADIUS request filter :");
2905 }
2906
2908 DEBUG2(" RADIUS response code : [%s]", fr_radius_packet_name[conf->filter_response_code]);
2909 }
2910
2911 if (conf->to_output_dir) {
2912 DEBUG2(" Writing packets in : [%s/{requests,reply}.${count}.txt]", conf->output_dir);
2913 }
2914
2916 DEBUG2(" RADIUS response filter :");
2918 }
2919 }
2920
2921 /*
2922 * Setup collectd templates
2923 */
2924#ifdef HAVE_COLLECTDC_H
2925 if (conf->stats.out == RS_STATS_OUT_COLLECTD) {
2926 size_t i;
2927 rs_stats_tmpl_t *tmpl, **next;
2928
2929 if (rs_stats_collectd_open(conf) < 0) {
2930 fr_exit_now(EXIT_FAILURE);
2931 }
2932
2933 next = &conf->stats.tmpl;
2934
2935 for (i = 0; i < (NUM_ELEMENTS(rs_useful_codes)); i++) {
2936 tmpl = rs_stats_collectd_init_latency(conf, next, conf, "exchanged",
2937 &(stats->exchange[rs_useful_codes[i]]),
2938 rs_useful_codes[i]);
2939 if (!tmpl) {
2940 ERROR("Error allocating memory for stats template");
2941 goto finish;
2942 }
2943 next = &(tmpl->next);
2944 }
2945 }
2946#endif
2947
2948 /*
2949 * This actually opens the capture interfaces/files (we just allocated the memory earlier)
2950 */
2951 {
2952 fr_pcap_t *tmp;
2953 fr_pcap_t **tmp_p = &tmp;
2954
2955 for (in_p = in;
2956 in_p;
2957 in_p = in_p->next) {
2958 in_p->promiscuous = conf->promiscuous;
2959 in_p->buffer_pkts = conf->buffer_pkts;
2960 if (fr_pcap_open(in_p) < 0) {
2961 fr_perror("Failed opening pcap handle (%s)", in_p->name);
2962 if (conf->from_auto || (in_p->type == PCAP_FILE_IN)) {
2963 continue;
2964 }
2965
2966 goto finish;
2967 }
2968
2969 if (!fr_pcap_link_layer_supported(in_p->link_layer)) {
2970 ERROR("Failed opening pcap handle (%s): Datalink type %s not supported",
2971 in_p->name, pcap_datalink_val_to_name(in_p->link_layer));
2972 goto finish;
2973 }
2974
2975 if (conf->pcap_filter) {
2976 /*
2977 * Not all link layers support VLAN tags
2978 * and this is the easiest way to discover
2979 * which do and which don't.
2980 */
2981 if ((!conf->pcap_filter_vlan ||
2982 (fr_pcap_apply_filter(in_p, conf->pcap_filter_vlan) < 0)) &&
2983 (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0)) {
2984 fr_perror("Failed applying filter");
2985 goto finish;
2986 }
2987 }
2988
2989 *tmp_p = in_p;
2990 tmp_p = &(in_p->next);
2991 }
2992 *tmp_p = NULL;
2993 in = tmp;
2994
2995 if (!in) {
2996 ERROR("No PCAP sources available");
2997 fr_exit_now(EXIT_FAILURE);
2998 }
2999
3000 /* Clear any irrelevant errors */
3002 }
3003
3004 /*
3005 * Open our output interface (if we have one);
3006 */
3007 if (out) {
3008 out->link_layer = -1; /* Infer output link type from input */
3009
3010 for (in_p = in;
3011 in_p;
3012 in_p = in_p->next) {
3013 if (out->link_layer < 0) {
3014 out->link_layer = in_p->link_layer;
3015 continue;
3016 }
3017
3018 if (out->link_layer != in_p->link_layer) {
3019 ERROR("Asked to write to output file, but inputs do not have the same link type");
3020 ret = 64;
3021 goto finish;
3022 }
3023 }
3024
3025 RS_ASSERT(out->link_layer >= 0);
3026
3027 if (fr_pcap_open(out) < 0) {
3028 fr_perror("Failed opening pcap output (%s)", out->name);
3029 goto finish;
3030 }
3031 }
3032
3033 /*
3034 * Get the offset between server time and wallclock time
3035 */
3036 fr_time_start();
3037
3038 /*
3039 * Setup and enter the main event loop. Who needs libev when you can roll your own...
3040 */
3041 {
3042 struct timeval now;
3043
3044 char *buff;
3045
3047 if (!events) {
3048 ERROR();
3049 goto finish;
3050 }
3051
3052 /*
3053 * Initialise the signal handler pipe
3054 */
3055 if (pipe(self_pipe) < 0) {
3056 ERROR("Couldn't open signal pipe: %s", fr_syserror(errno));
3057 fr_exit_now(EXIT_FAILURE);
3058 }
3059
3060 if (fr_event_fd_insert(NULL, NULL, events, self_pipe[0],
3062 NULL,
3063 NULL,
3064 events) < 0) {
3065 fr_perror("Failed inserting signal pipe descriptor");
3066 goto finish;
3067 }
3068
3069 buff = fr_pcap_device_names(conf, in, ' ');
3070 DEBUG("Sniffing on (%s)", buff);
3071
3072 /*
3073 * Insert our stats processor
3074 */
3075 if (conf->stats.interval && conf->from_dev) {
3076 now = fr_time_to_timeval(fr_time());
3077 rs_install_stats_processor(stats, events, in, &now, false);
3078 }
3079
3080 /*
3081 * Now add fd's for each of the pcap sessions we opened
3082 */
3083 for (in_p = in;
3084 in_p;
3085 in_p = in_p->next) {
3086 rs_event_t *event;
3087
3088 event = talloc_zero(events, rs_event_t);
3089 event->list = events;
3090 event->in = in_p;
3091 event->out = out;
3092 event->stats = stats;
3093
3094 /*
3095 * kevent() doesn't indicate that the
3096 * file is readable if there's not
3097 * sufficient packets in the file.
3098 *
3099 * Work around this by processing
3100 * files immediately, and only inserting
3101 * "live" inputs, i.e. stdin and
3102 * actual pcap sockets into the
3103 * event loop.
3104 */
3105 if (event->in->type == PCAP_FILE_IN) {
3106 rs_got_packet(events, in_p->fd, 0, event);
3107 } else if (fr_event_fd_insert(NULL, NULL, events, in_p->fd,
3109 NULL,
3110 NULL,
3111 event) < 0) {
3112 ERROR("Failed inserting file descriptor");
3113 goto finish;
3114 }
3115 }
3116
3117 if (timeout) {
3118 if (fr_timer_in(NULL, events->tl, &timeout_ev, fr_time_delta_from_sec(timeout),
3119 false, timeout_event, events) < 0) {
3120 ERROR("Failed inserting timeout event");
3121 }
3122 }
3123 }
3124
3125 /*
3126 * If we just have the pipe, then exit.
3127 */
3128 if (fr_event_list_num_fds(events) == 1) goto finish;
3129
3130 /*
3131 * Do this as late as possible so we can return an error code if something went wrong.
3132 */
3133 if (conf->daemonize) {
3135 }
3136
3137 /*
3138 * Setup signal handlers so we always exit gracefully, ensuring output buffers are always
3139 * flushed.
3140 */
3141 fr_set_signal(SIGPIPE, rs_signal_self);
3143 fr_set_signal(SIGTERM, rs_signal_self);
3144#ifdef SIGQUIT
3145 fr_set_signal(SIGQUIT, rs_signal_self);
3146#endif
3147 DEBUG2("Entering event loop");
3148
3149 fr_event_loop(events); /* Enter the main event loop */
3150
3151 DEBUG2("Done sniffing");
3152
3153finish:
3154 cleanup = true;
3155
3156 if (conf->daemonize) unlink(conf->pidfile);
3157
3158 /*
3159 * Free all the things! This also closes all the sockets and file descriptors
3160 */
3162
3165
3166 /*
3167 * Ensure our atexit handlers run before any other
3168 * atexit handlers registered by third party libraries.
3169 */
3171
3172 return ret;
3173}
static int const char char buffer[256]
Definition acutest.h:576
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:160
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:286
#define fr_base16_encode(_out, _in)
Definition base16.h:57
#define RCSID(id)
Definition build.h:485
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:315
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:121
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
Definition dcursor.h:353
static void * fr_dcursor_filter_next(fr_dcursor_t *cursor, fr_dcursor_eval_t eval, void const *uctx)
Return the next item, skipping the current item, that satisfies an evaluation function.
Definition dcursor.h:545
static void * fr_dcursor_remove(fr_dcursor_t *cursor)
Remove the current item.
Definition dcursor.h:480
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1242
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:234
fr_radius_packet_code_t
RADIUS packet codes.
Definition defs.h:31
@ FR_RADIUS_CODE_ACCESS_CHALLENGE
RFC2865 - Access-Challenge.
Definition defs.h:43
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
Definition defs.h:33
@ FR_RADIUS_CODE_DISCONNECT_REQUEST
RFC3575/RFC5176 - Disconnect-Request.
Definition defs.h:46
@ FR_RADIUS_CODE_DISCONNECT_ACK
RFC3575/RFC5176 - Disconnect-Ack (positive)
Definition defs.h:47
@ FR_RADIUS_CODE_STATUS_SERVER
RFC2865/RFC5997 - Status Server (request)
Definition defs.h:44
@ FR_RADIUS_CODE_COA_REQUEST
RFC3575/RFC5176 - CoA-Request.
Definition defs.h:49
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
Definition defs.h:34
@ FR_RADIUS_CODE_ACCOUNTING_RESPONSE
RFC2866 - Accounting-Response.
Definition defs.h:37
@ FR_RADIUS_CODE_COA_NAK
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
Definition defs.h:51
@ FR_RADIUS_CODE_COA_ACK
RFC3575/RFC5176 - CoA-Ack (positive)
Definition defs.h:50
@ FR_RADIUS_CODE_DISCONNECT_NAK
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
Definition defs.h:48
@ FR_RADIUS_CODE_ACCOUNTING_REQUEST
RFC2866 - Accounting-Request.
Definition defs.h:36
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
Definition defs.h:35
#define FR_COA_UDP_PORT
Definition defs.h:63
#define FR_AUTH_UDP_PORT
Definition defs.h:57
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:114
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir)
Initialise the global protocol hashes.
Definition dict_util.c:4399
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4592
#define fr_dict_autofree(_to_free)
Definition dict.h:860
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3270
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2407
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:272
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:285
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4097
#define fr_dict_autoload(_to_load)
Definition dict.h:857
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename)
Read supplementary attribute definitions into an existing dictionary.
static fr_slen_t in
Definition dict.h:831
Specifies an attribute which must be present for the module to function.
Definition dict.h:271
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:284
#define fr_event_fd_insert(...)
Definition event.h:248
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:84
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:219
int fr_unlink(char const *filename)
Remove a regular file from the filesystem.
Definition file.c:367
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition inet.c:1346
talloc_free(reap)
uint64_t fr_event_list_num_fds(fr_event_list_t *el)
Return the number of file descriptors is_registered with this event loop.
Definition event.c:551
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2526
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
Definition event.c:2386
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2375
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1206
int fr_event_loop(fr_event_list_t *el)
Run an event loop.
Definition event.c:2397
Stores all information relating to an event list.
Definition event.c:380
int fr_debug_lvl
Definition log.c:43
FILE * fr_log_fp
Definition log.c:42
fr_log_t default_log
Definition log.c:291
int fr_log_init_file(fr_log_t *log, char const *file)
Initialise a file logging destination.
Definition log.c:1083
int fr_log_close(fr_log_t *log)
Universal close function for all logging destinations.
Definition log.c:1189
@ L_TIMESTAMP_OFF
Never log timestamps.
Definition log.h:91
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition log.h:71
@ L_DBG_LVL_4
4th highest priority debug messages (-xxxx | -Xxx).
Definition log.h:73
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition packet.c:38
fr_packet_t * fr_packet_alloc_reply(TALLOC_CTX *ctx, fr_packet_t *packet)
Allocate a new fr_packet_t response.
Definition packet.c:63
void fr_packet_free(fr_packet_t **packet_p)
Free a fr_packet_t.
Definition packet.c:89
int8_t fr_packet_cmp(void const *a_v, void const *b_v)
Definition list.c:43
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
void fr_quick_sort(void const *to_sort[], int start, int end, fr_cmp_t cmp)
Quick sort an array of pointers using a comparator.
Definition misc.c:429
int fr_set_signal(int sig, sig_t func)
Sets a signal handler using sigaction if available, else signal.
Definition misc.c:47
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
Definition misc.c:417
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
Definition missing.c:443
char * strsep(char **stringp, char const *delim)
Definition missing.c:123
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:163
uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum, struct in_addr const src_addr, struct in_addr const dst_addr)
Calculate UDP checksum.
Definition net.c:119
uint16_t len
UDP length.
Definition net.h:141
struct in6_addr ip_src ip_dst
Src and Dst address.
Definition net.h:125
#define RADIUS_AUTH_VECTOR_LENGTH
Definition net.h:89
uint16_t dst
Destination port.
Definition net.h:140
uint16_t src
Source port.
Definition net.h:139
uint16_t checksum
UDP checksum.
Definition net.h:142
struct in_addr ip_src ip_dst
Src and Dst address.
Definition net.h:115
uint8_t ip_vhl
Header length, version.
Definition net.h:105
bool fr_pair_matches_da(void const *item, void const *uctx)
Evaluation function for matching if vp matches a given da.
Definition pair.c:3385
int fr_pair_list_cmp(fr_pair_list_t const *a, fr_pair_list_t const *b)
Determine equality of two lists.
Definition pair.c:2049
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:695
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1347
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
int fr_pair_steal(TALLOC_CTX *ctx, fr_pair_t *vp)
Steal one VP.
Definition pair.c:523
bool fr_pair_validate_relaxed(fr_pair_t const *failed[2], fr_pair_list_t *filter, fr_pair_list_t *list)
Uses fr_pair_cmp to verify all fr_pair_ts in list match the filter defined by check.
Definition pair.c:2207
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:491
int8_t fr_pair_cmp_by_da(void const *a, void const *b)
Order attributes by their da, and tag.
Definition pair.c:1846
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
struct fr_pair_parse_s fr_pair_parse_t
TALLOC_CTX * ctx
Definition pair_legacy.h:43
int fr_radius_global_init(void)
Definition base.c:1212
ssize_t fr_radius_decode_simple(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t *packet, size_t packet_len, uint8_t const *vector, char const *secret)
Simple wrapper for callers who just need a shared secret.
Definition base.c:1190
void fr_radius_global_free(void)
Definition base.c:1235
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition base.c:112
int fr_packet_verify(fr_packet_t *packet, fr_packet_t *original, char const *secret)
Verify the Request/Response Authenticator (and Message-Authenticator if present) of a packet.
Definition packet.c:143
bool fr_packet_ok(fr_packet_t *packet, uint32_t max_attributes, bool require_message_authenticator, fr_radius_decode_fail_t *reason)
See if the data pointed to by PTR is a valid RADIUS packet.
Definition packet.c:119
static TALLOC_CTX * autofree
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG(fmt,...)
Definition radclient.h:53
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define INFO(fmt,...)
Definition radict.c:54
fr_radius_decode_fail_t
Failure reasons.
Definition radius.h:162
#define RADIUS_MAX_ATTRIBUTES
Definition radius.h:40
#define fr_packet_log_hex(_log, _packet)
Definition radius.h:271
#define FR_RADIUS_PACKET_CODE_VALID(_x)
Definition radius.h:52
fr_dict_attr_autoload_t radsniff_dict_attr[]
Definition radsniff.c:106
static int rs_request_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header, uint8_t const *data)
Definition radsniff.c:1202
static void timeout_event(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now_t, void *ctx)
Exit the event loop after a given timeout.
Definition radsniff.c:2178
static struct timeval start_pcap
Definition radsniff.c:54
static size_t rs_snprint_csv(char *out, size_t outlen, char const *in, size_t inlen)
Definition radsniff.c:220
static fr_rb_tree_t * link_tree
Definition radsniff.c:58
static void rs_packet_print(rs_request_t *request, uint64_t count, rs_status_t status, fr_pcap_t *handle, fr_packet_t *packet, fr_pair_list_t *list, struct timeval *elapsed, struct timeval *latency, bool response, bool body)
Definition radsniff.c:529
static int self_pipe[2]
Signals from sig handlers.
Definition radsniff.c:63
#define RS_ASSERT(_x)
Definition radsniff.c:51
int main(int argc, char *argv[])
Definition radsniff.c:2321
static fr_dict_attr_t const * attr_packet_type
Definition radsniff.c:103
static void rs_stats_process_latency(rs_latency_t *stats)
Update smoothed average.
Definition radsniff.c:580
static ssize_t rs_stats_print_code_csv(char *out, size_t outlen, rs_latency_t *stats)
Definition radsniff.c:774
static void rs_stats_print_code_fancy(rs_latency_t *stats, fr_radius_packet_code_t code)
Definition radsniff.c:635
static void rs_daemonize(char const *pidfile)
Fork and kill the parent process, writing out our PID.
Definition radsniff.c:117
static void rs_stats_process(fr_timer_list_t *tl, fr_time_t now_t, void *ctx)
Process stats for a single interval.
Definition radsniff.c:844
static int rs_install_stats_processor(rs_stats_t *stats, fr_event_list_t *el, fr_pcap_t *in, struct timeval *now, bool live)
Definition radsniff.c:940
static fr_event_list_t * events
Definition radsniff.c:59
static const uint8_t zeros[RADIUS_AUTH_VECTOR_LENGTH]
Definition radsniff.c:1262
static void rs_packet_save_in_output_dir(uint64_t count, UNUSED rs_status_t status, UNUSED fr_pcap_t *handle, fr_packet_t *packet, fr_pair_list_t *list, UNUSED struct timeval *elapsed, UNUSED struct timeval *latency, bool response, bool body)
Definition radsniff.c:478
static void rs_packet_print_csv_header(void)
Definition radsniff.c:268
static int packets_count
Definition radsniff.c:61
static fr_dict_t const * dict_freeradius
Definition radsniff.c:93
static int rs_response_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header, uint8_t const *data)
Definition radsniff.c:1152
static fr_rb_tree_t * request_tree
Definition radsniff.c:57
static fr_dict_t const * dict_radius
Definition radsniff.c:94
static rs_request_t * rs_request_alloc(TALLOC_CTX *ctx)
Definition radsniff.c:1248
static bool cleanup
Definition radsniff.c:60
static int _rs_event_status(UNUSED fr_time_t now, fr_time_delta_t wake_t, UNUSED void *uctx)
Definition radsniff.c:2022
static char const * radsniff_version
Definition radsniff.c:65
static void _unmark_request(void *request)
Callback for when the request is removed from the request tree.
Definition radsniff.c:2159
static int rs_build_event_flags(int *flags, fr_table_num_sorted_t const *map, size_t map_len, char *list)
Definition radsniff.c:2130
static int8_t rs_rtx_cmp(void const *one, void const *two)
Compare requests using packet info and lists of attributes.
Definition radsniff.c:2042
static void rs_stats_print_csv(rs_update_t *this, rs_stats_t *stats, UNUSED struct timeval *now)
Definition radsniff.c:799
static int rs_check_pcap_drop(fr_pcap_t *in)
Query libpcap to see if it dropped any packets.
Definition radsniff.c:552
static void rs_stats_print_fancy(rs_update_t *this, rs_stats_t *stats, struct timeval *now)
Definition radsniff.c:679
static size_t rs_events_len
Definition radsniff.c:91
static int rs_build_dict_list(fr_dict_attr_t const **out, size_t len, char *list)
Definition radsniff.c:2064
fr_dict_autoload_t radsniff_dict[]
Definition radsniff.c:97
static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result)
Definition radsniff.c:183
static void rs_time_print(char *out, size_t len, struct timeval const *t)
Definition radsniff.c:193
static void rs_signal_self(int sig)
Write the last signal to the signal pipe.
Definition radsniff.c:2212
static void rs_packet_cleanup(rs_request_t *request)
Definition radsniff.c:1063
static void _unmark_link(void *request)
Callback for when the request is removed from the link tree.
Definition radsniff.c:2169
static void rs_stats_print_csv_header(rs_update_t *this)
Definition radsniff.c:726
static char timestr[50]
Definition radsniff.c:55
#define RS_CLEANUP_NOW(_x, _s)
Definition radsniff.c:1240
static void rs_got_packet(fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
Definition radsniff.c:1929
static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle, fr_packet_t *packet, fr_pair_list_t *list, struct timeval *elapsed, struct timeval *latency, bool response, bool body)
Definition radsniff.c:383
static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
Definition radsniff.c:1264
static int8_t rs_packet_cmp(void const *one, void const *two)
Wrapper around fr_packet_cmp to strip off the outer request struct.
Definition radsniff.c:1144
static int rs_get_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_pair_list_t *vps, fr_dict_attr_t const *da[], int num)
Copy a subset of attributes from one list into the other.
Definition radsniff.c:996
static void rs_signal_action(UNUSED fr_event_list_t *list, int fd, int UNUSED flags, UNUSED void *ctx)
Read the last signal from the signal pipe.
Definition radsniff.c:2223
static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle, fr_packet_t *packet, fr_pair_list_t *list, UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response, bool body)
Definition radsniff.c:309
static void rs_stats_process_counters(rs_latency_t *stats)
Definition radsniff.c:620
static int rs_useful_codes[]
Definition radsniff.c:67
static rs_t * conf
Definition radsniff.c:53
static int _request_free(rs_request_t *request)
Definition radsniff.c:1030
static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
Update latency statistics for request/response and forwarded packets.
Definition radsniff.c:923
static int rs_build_filter(fr_pair_list_t *out, char const *filter)
Definition radsniff.c:2100
static void _rs_event(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *ctx)
Definition radsniff.c:1134
static fr_table_num_sorted_t const rs_events[]
Definition radsniff.c:83
Structures and prototypes for the RADIUS sniffer.
fr_radius_packet_code_t filter_response_code
Filter response packets by code.
Definition radsniff.h:300
fr_pcap_t * in
PCAP handle event occurred on.
Definition radsniff.h:227
fr_packet_t * expect
Request/response.
Definition radsniff.h:195
bool to_output_dir
Were writing attributes into directory.
Definition radsniff.h:266
struct rs::@1 stats
rs_stats_print_cb_t body
Print body.
Definition radsniff.h:256
bool print_packet
Print packet info, disabled with -W.
Definition radsniff.h:274
uint8_t * data
PCAP packet data.
Definition radsniff.h:176
fr_dict_attr_t const * list_da[RS_MAX_ATTRS]
Output CSV with these attribute values.
Definition radsniff.h:287
char const * output_dir
Where we should save the files $PATH/requests.txt and $PATH/reply.txt.
Definition radsniff.h:267
fr_timer_t * event
Event created when we received the original request.
Definition radsniff.h:186
@ RS_STATS_OUT_STDIO_CSV
Definition radsniff.h:86
@ RS_STATS_OUT_STDIO_FANCY
Definition radsniff.h:85
#define RS_DEFAULT_SECRET
Default secret.
Definition radsniff.h:41
bool in_request_tree
Whether the request is currently in the request tree.
Definition radsniff.h:217
rs_status_t event_flags
Events we log and capture on.
Definition radsniff.h:302
fr_dict_attr_t const * link_da[RS_MAX_ATTRS]
fr_dict_attr_ts to link on.
Definition radsniff.h:291
#define RIDEBUG(fmt,...)
Definition radsniff.h:65
uint64_t latency_smoothed_count
Number of CMA datapoints processed.
Definition radsniff.h:116
int link_da_num
Number of rtx fr_dict_attr_ts.
Definition radsniff.h:292
fr_radius_packet_code_t filter_request_code
Filter request packets by code.
Definition radsniff.h:299
rs_stats_t * stats
Stats to process.
Definition radsniff.h:254
int buffer_pkts
Size of the ring buffer to setup for live capture.
Definition radsniff.h:305
char const * filter_response
Raw response filter string.
Definition radsniff.h:295
fr_event_list_t * list
List to insert new event into.
Definition radsniff.h:251
int list_da_num
Definition radsniff.h:288
struct timeval when
Time when the packet was received, or next time an event is scheduled.
Definition radsniff.h:190
bool from_dev
Were reading pcap data from devices.
Definition radsniff.h:261
#define RS_FORCE_YIELD
Service another descriptor every X number of packets.
Definition radsniff.h:43
#define RS_DEFAULT_TIMEOUT
Standard timeout of 5s + 300ms to cover network latency.
Definition radsniff.h:42
rs_packet_logger_t logger
Packet logger.
Definition radsniff.h:303
char * pcap_filter_vlan
Variant of the normal filter to apply to devices which support VLAN tags.
Definition radsniff.h:283
char * pcap_filter
PCAP filter string applied to live capture devices.
Definition radsniff.h:282
rs_capture_t capture[RS_RETRANSMIT_MAX]
Buffered request packets (if a response filter has been applied).
Definition radsniff.h:201
bool from_auto
From list was auto-generated.
Definition radsniff.h:272
rs_latency_t * stats_req
Latency entry for the request type.
Definition radsniff.h:208
bool decode_attrs
Whether we should decode attributes in the request and response.
Definition radsniff.h:275
uint64_t id
Monotonically increasing packet counter.
Definition radsniff.h:185
rs_latency_t * stats_rsp
Latency entry for the request type.
Definition radsniff.h:209
bool to_file
Were writing pcap data to files.
Definition radsniff.h:263
fr_pair_list_t link_vps
fr_pair_ts used to link retransmissions.
Definition radsniff.h:199
fr_packet_t * packet
The original packet.
Definition radsniff.h:193
#define RS_RETRANSMIT_MAX
Maximum number of times we expect to see a packet retransmitted.
Definition radsniff.h:44
rs_stats_print_header_cb_t head
Print header.
Definition radsniff.h:255
char * list_attributes
Raw attribute filter string.
Definition radsniff.h:286
char * radius_secret
Secret to decode encrypted attributes.
Definition radsniff.h:280
bool from_file
Were reading pcap data from files.
Definition radsniff.h:260
fr_pcap_t * out
Where to write output.
Definition radsniff.h:228
#define RS_DEFAULT_PREFIX
Default instance.
Definition radsniff.h:40
fr_pcap_t * in
PCAP handle the original request was received on.
Definition radsniff.h:192
fr_pcap_t * in
Linked list of PCAP handles to check for drops.
Definition radsniff.h:253
rs_capture_t * capture_p
Next packet slot.
Definition radsniff.h:203
bool in_link_tree
Whether the request is currently in the linked tree.
Definition radsniff.h:218
struct timeval quiet
We may need to 'mute' the stats if libpcap starts dropping packets, or we run out of memory.
Definition radsniff.h:170
#define RIDEBUG_ENABLED()
Definition radsniff.h:60
int intervals
Number of stats intervals.
Definition radsniff.h:163
fr_event_list_t * list
The event list.
Definition radsniff.h:225
fr_packet_t * linked
The subsequent response or forwarded request the packet was linked against.
Definition radsniff.h:197
bool verify_radius_authenticator
Check RADIUS authenticator in packets.
Definition radsniff.h:278
uint64_t limit
Maximum number of packets to capture.
Definition radsniff.h:306
char const * pidfile
File to write PID to.
Definition radsniff.h:270
uint64_t rt_rsp
Number of times we saw a retransmitted response packet.
Definition radsniff.h:206
bool from_stdin
Were reading pcap data from stdin.
Definition radsniff.h:262
bool daemonize
Daemonize and write PID out to file.
Definition radsniff.h:269
double latency_smoothed
Smoothed moving average.
Definition radsniff.h:115
uint64_t rt_req
Number of times we saw the same request packet.
Definition radsniff.h:205
rs_latency_t exchange[FR_RADIUS_CODE_MAX+1]
We end up allocating ~16K, but memory is cheap so.
Definition radsniff.h:165
fr_pair_list_t filter_response_vps
Sorted filter vps.
Definition radsniff.h:298
bool promiscuous
Capture in promiscuous mode.
Definition radsniff.h:273
fr_pair_list_t expect_vps
Definition radsniff.h:196
fr_pair_list_t packet_vps
Definition radsniff.h:194
bool logged
Whether any messages regarding this request were logged.
Definition radsniff.h:188
char * link_attributes
Names of fr_dict_attr_ts to use for rtx.
Definition radsniff.h:290
bool silent_cleanup
Cleanup was forced before normal expiry period, ignore stats about packet loss.
Definition radsniff.h:211
char const * filter_request
Raw request filter string.
Definition radsniff.h:294
rs_status_t
Definition radsniff.h:69
@ RS_ERROR
Definition radsniff.h:74
@ RS_UNLINKED
Definition radsniff.h:71
@ RS_REUSED
Definition radsniff.h:73
@ RS_LOST
Definition radsniff.h:75
@ RS_NORMAL
Definition radsniff.h:70
@ RS_RTX
Definition radsniff.h:72
struct pcap_pkthdr * header
PCAP packet header.
Definition radsniff.h:175
#define RS_SOCKET_REOPEN_DELAY
How long we delay re-opening a collectd socket.
Definition radsniff.h:46
bool verify_udp_checksum
Check UDP checksum in packets.
Definition radsniff.h:277
bool to_stdout
Were writing pcap data to stdout.
Definition radsniff.h:264
struct rs_latency_t::@0 interval
fr_pair_list_t filter_request_vps
Sorted filter vps.
Definition radsniff.h:297
Definition radsniff.h:259
Statistic write/print event.
Definition radsniff.h:224
Stats for a single interval.
Definition radsniff.h:112
Wrapper for fr_packet_t.
Definition radsniff.h:184
One set of statistics.
Definition radsniff.h:162
FD data which gets passed to callbacks.
Definition radsniff.h:249
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:246
The main red black tree structure.
Definition rb.h:73
static char const * name
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1595
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_current(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define fr_sbuff_remaining(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_in_char(_sbuff,...)
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
return count
Definition module.c:163
fr_aka_sim_id_type_t type
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Definition log.h:96
fr_log_timestamp_t timestamp
Prefix log messages with timestamps.
Definition log.h:110
bool print_level
sometimes we don't want log levels printed
Definition log.h:106
FILE * handle
Path to log file.
Definition log.h:116
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
int fr_time_sync(void)
Get a new fr_time_monotonic_to_realtime value.
Definition time.c:102
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
Simple time functions.
static fr_time_t fr_time_from_timeval(struct timeval const *when_tv)
Convert a timeval (wallclock time) to a fr_time_t (internal time)
Definition time.h:896
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_delta_to_timeval(_delta)
Convert a delta to a timeval.
Definition time.h:656
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_to_timeval(_when)
Convert server epoch time to unix epoch time.
Definition time.h:742
#define USEC
Definition time.h:380
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
#define fr_time_delta_gt(_a, _b)
Definition time.h:283
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
Definition timer.c:815
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
Definition timer.c:643
An event timer list.
Definition timer.c:53
A timer event.
Definition timer.c:79
#define fr_timer_in(...)
Definition timer.h:86
#define fr_timer_at(...)
Definition timer.h:80
void fr_timeval_subtract(struct timeval *out, struct timeval const *end, struct timeval const *start)
Subtract one timeval from another.
Definition timeval.c:36
@ T_BARE_WORD
Definition token.h:120
close(uq->fd)
static fr_event_list_t * el
static void print_packet(FILE *fp, fr_packet_t *packet, fr_pair_list_t *list)
static fr_slen_t head
Definition xlat.h:418
#define FR_DICTIONARY_FILE
Definition conf.h:7
unsigned int code
Packet code (type).
Definition packet.h:61
fr_socket_t socket
This packet was received on.
Definition packet.h:57
int id
Packet ID (used to link requests/responses).
Definition packet.h:60
uint8_t * data
Packet data (body).
Definition packet.h:63
size_t data_len
Length of packet data.
Definition packet.h:64
uint8_t vector[RADIUS_AUTH_VECTOR_LENGTH]
RADIUS authentication vector.
Definition packet.h:69
fr_time_t timestamp
When we received the packet.
Definition packet.h:58
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition pair.h:628
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
void fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:70
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition pair_print.c:53
#define fr_pair_list_log(_log, _lvl, _list)
Definition pair.h:851
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:591
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:43
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition socket.h:78
int type
SOCK_STREAM, SOCK_DGRAM, etc.
Definition socket.h:79
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:554
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:733
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:577
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
Definition version.h:58
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
static fr_slen_t data
Definition value.h:1274
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1012
static size_t char ** out
Definition value.h:1012