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