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