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