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