27 RCSID(
"$Id: 118c1a00ea31b674d6228b3aef49a9308da7b115 $")
32 #include <freeradius-devel/libradius.h>
33 #include <freeradius-devel/event.h>
35 #include <freeradius-devel/radpaths.h>
36 #include <freeradius-devel/conf.h>
37 #include <freeradius-devel/pcap.h>
38 #include <freeradius-devel/radsniff.h>
40 #ifdef HAVE_COLLECTDC_H
41 # include <collectd/client.h>
44 #define RS_ASSERT(_x) if (!(_x) && !fr_assert(_x)) exit(1)
57 typedef int (*
rbcmp)(
void const *,
void const *);
60 #ifdef RADIUSD_VERSION_COMMIT
61 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT)
")"
63 ", built on " __DATE__
" at " __TIME__;
131 if ((chdir(
"/")) < 0) {
139 fp = fopen(pidfile,
"w");
141 fprintf(fp,
"%d\n", (
int) sid);
151 if (isatty(fileno(stdout))) {
152 if (!freopen(
"/dev/null",
"w", stdout)) {
157 if (isatty(fileno(stderr))) {
158 if (!freopen(
"/dev/null",
"w", stderr)) {
165 static void rs_tv_sub(
struct timeval
const *end,
struct timeval
const *start,
struct timeval *elapsed)
167 elapsed->tv_sec = end->tv_sec - start->tv_sec;
168 if (elapsed->tv_sec > 0) {
170 elapsed->tv_usec =
USEC;
172 elapsed->tv_usec = 0;
174 elapsed->tv_usec += end->tv_usec;
175 elapsed->tv_usec -= start->tv_usec;
177 if (elapsed->tv_usec >=
USEC) {
178 elapsed->tv_usec -=
USEC;
183 static void rs_tv_add_ms(
struct timeval
const *start,
unsigned long interval,
struct timeval *result) {
184 result->tv_sec = start->tv_sec + (interval / 1000);
185 result->tv_usec = start->tv_usec + ((interval % 1000) * 1000);
187 if (result->tv_usec >
USEC) {
188 result->tv_usec -=
USEC;
200 gettimeofday(&now, NULL);
204 ret = strftime(out, len,
"%Y-%m-%d %H:%M:%S", localtime(&t->tv_sec));
212 while (usec < 100000) usec *= 10;
213 snprintf(out + ret, len - ret,
".%i", usec);
215 snprintf(out + ret, len - ret,
".000000");
219 static size_t rs_snprint_csv(
char *out,
size_t outlen,
char const *in,
size_t inlen)
221 char const *start = out;
222 uint8_t
const *str = (uint8_t
const *) in;
236 while ((inlen > 0) && (outlen > 2)) {
248 if ((*str ==
'\r') || (*str ==
'\n') || ((*str >=
'\x20') && (*str <=
'\x7E'))) {
273 ssize_t len, s =
sizeof(buffer);
275 len =
strlcpy(p,
"\"Status\",\"Count\",\"Time\",\"Latency\",\"Type\",\"Interface\","
276 "\"Src IP\",\"Src Port\",\"Dst IP\",\"Dst Port\",\"ID\",", s);
305 fprintf(stdout ,
"%s\n", buffer);
309 UNUSED struct timeval *elapsed,
struct timeval *latency,
UNUSED bool response,
312 char const *status_str;
316 char src[INET6_ADDRSTRLEN];
317 char dst[INET6_ADDRSTRLEN];
319 ssize_t len, s =
sizeof(buffer);
324 status_str =
fr_int2str(rs_events, status, NULL);
327 len =
snprintf(p, s,
"%s,%" PRIu64
",%s,", status_str, count, timestr);
335 (
unsigned int) latency->tv_sec, ((
unsigned int) latency->tv_usec / 1000));
348 len =
snprintf(p, s,
"%s,%s,%s,%i,%s,%i,%i,",
fr_packet_codes[packet->
code], handle->name,
351 len =
snprintf(p, s,
"%u,%s,%s,%i,%s,%i,%i,", packet->
code, handle->name,
365 if (vp && (vp->vp_length > 0)) {
400 fprintf(stdout ,
"%s\n", buffer);
404 struct timeval *elapsed,
struct timeval *latency,
bool response,
bool body)
409 char src[INET6_ADDRSTRLEN];
410 char dst[INET6_ADDRSTRLEN];
412 ssize_t len, s =
sizeof(buffer);
419 char const *status_str;
421 status_str =
fr_int2str(rs_events, status, NULL);
424 len =
snprintf(p, s,
"** %s ** ", status_str);
431 len =
snprintf(p, s,
"%s Id %i %s:%s:%d %s %s:%i ",
435 response ? dst : src,
437 response ?
"<-" :
"->",
438 response ? src : dst ,
441 len =
snprintf(p, s,
"%u Id %i %s:%s:%i %s %s:%i ",
445 response ? dst : src,
447 response ?
"<-" :
"->",
448 response ? src : dst ,
457 (
unsigned int) elapsed->tv_sec, ((
unsigned int) elapsed->tv_usec / 1000));
465 (
unsigned int) latency->tv_sec, ((
unsigned int) latency->tv_usec / 1000));
492 INFO(
"\tAuthenticator-Field = 0x%s", vector);
498 RADIUS_PACKET *packet,
struct timeval *elapsed,
struct timeval *latency,
499 bool response,
bool body)
501 if (!conf->
logger)
return;
503 if (request) request->
logged =
true;
504 conf->
logger(count, status, handle, packet, elapsed, latency, response, body);
522 struct pcap_stat pstats;
524 if (pcap_stats(in->handle, &pstats) != 0) {
525 ERROR(
"%s failed retrieving pcap stats: %s", in->name, pcap_geterr(in->handle));
529 if (pstats.ps_drop - in->pstats.ps_drop > 0) {
530 ERROR(
"%s dropped %i packets: Buffer exhaustion", in->name, pstats.ps_drop - in->pstats.ps_drop);
534 if (pstats.ps_ifdrop - in->pstats.ps_ifdrop > 0) {
535 ERROR(
"%s dropped %i packets: Interface", in->name, pstats.ps_ifdrop - in->pstats.ps_ifdrop);
557 if (stats->
interval.linked_total == 0) {
558 double unk = strtod(
"NAN()", (
char **) NULL);
560 stats->
interval.latency_average = unk;
580 if (stats->
interval.latency_average > 0) {
605 bool have_rt =
false;
609 if (!stats->
interval.received && !have_rt && !stats->
interval.reused)
return;
624 INFO(
"\tAverage : %.3lfms", stats->
interval.latency_average);
635 if (!stats->
interval.rt[i])
continue;
650 size_t rs_codes_len = (
sizeof(
rs_useful_codes) /
sizeof(*rs_useful_codes));
658 if ((stats->
quiet.tv_sec + (stats->
quiet.tv_usec / 1000000.0)) -
659 (now->tv_sec + (now->tv_usec / 1000000.0)) > 0) {
660 INFO(
"Stats muted because of warmup, or previous error");
664 INFO(
"######### Stats Iteration %i #########", stats->
intervals);
666 if (this->in)
INFO(
"Interface capture rate:");
667 for (in_p = this->in;
670 struct pcap_stat pstats;
672 if (pcap_stats(in_p->handle, &pstats) != 0) {
673 ERROR(
"%s failed retrieving pcap stats: %s", in_p->name, pcap_geterr(in_p->handle));
677 INFO(
"\t%s%*s: %.3lf/s", in_p->name, (
int) (10 - strlen(in_p->name)),
"",
678 ((
double) (pstats.ps_recv - in_p->pstats.ps_recv)) / conf->
stats.interval);
686 for (i = 0; i < rs_codes_len; i++) {
696 size_t rs_codes_len = (
sizeof(
rs_useful_codes) /
sizeof(*rs_useful_codes));
700 fprintf(stdout,
"\"Iteration\"");
702 for (in_p = this->in; in_p; in_p = in_p->next) {
703 fprintf(stdout,
",\"%s PPS\"", in_p->name);
706 for (i = 0; i < rs_codes_len; i++) {
713 ",\"%s lat high (ms)\""
714 ",\"%s lat low (ms)\""
715 ",\"%s lat avg (ms)\""
716 ",\"%s lat ma (ms)\""
731 fprintf(stdout,
",\"%s rtx (%i)\"", name, j);
733 fprintf(stdout,
",\"%s rtx (%i+)\"", name, j);
738 fprintf(stdout ,
"\n");
744 char *p = out, *end = out + outlen;
746 p +=
snprintf(out, outlen,
",%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf",
756 if (p >= end)
return -1;
760 if (p >= end)
return -1;
768 char buffer[2048], *p = buffer, *end = buffer +
sizeof(buffer);
771 size_t rs_codes_len = (
sizeof(
rs_useful_codes) /
sizeof(*rs_useful_codes));
776 ERROR(
"Exceeded line buffer size");
780 for (in_p = this->in;
783 struct pcap_stat pstats;
785 if (pcap_stats(in_p->handle, &pstats) != 0) {
786 ERROR(
"%s failed retrieving pcap stats: %s", in_p->name, pcap_geterr(in_p->handle));
790 p +=
snprintf(p,
sizeof(buffer) - (p - buffer),
",%.3lf",
791 ((
double) (pstats.ps_recv - in_p->pstats.ps_recv)) / conf->
stats.interval);
792 if (p >= end)
goto oob;
795 for (i = 0; i < rs_codes_len; i++) {
799 if (slen < 0)
goto oob;
802 if (p >= end)
goto oob;
805 fprintf(stdout ,
"%s\n", buffer);
814 size_t rs_codes_len = (
sizeof(
rs_useful_codes) /
sizeof(*rs_useful_codes));
819 if (!this->done_header) {
820 if (this->head) this->head(
this);
821 this->done_header =
true;
826 for (in_p = this->in;
830 ERROR(
"Muting stats for the next %i milliseconds", conf->
stats.timeout);
840 if ((stats->
quiet.tv_sec + (stats->
quiet.tv_usec / 1000000.0)) -
841 (now->tv_sec + (now->tv_usec / 1000000.0)) > 0)
goto clear;
843 for (i = 0; i < rs_codes_len; i++) {
848 if (this->body) this->body(
this, stats, now);
850 #ifdef HAVE_COLLECTDC_H
855 if ((conf->
stats.out == RS_STATS_OUT_COLLECTD) && conf->
stats.handle) {
856 rs_stats_collectd_do_stats(conf, conf->
stats.tmpl, now);
864 for (i = 0; i < rs_codes_len; i++) {
872 now->tv_sec += conf->
stats.interval;
876 ERROR(
"Failed inserting stats interval event");
891 lint = (latency->tv_sec + (latency->tv_usec / 1000000.0)) * 1000;
892 if (lint > stats->
interval.latency_high) {
893 stats->
interval.latency_high = lint;
895 if (!stats->
interval.latency_low || (lint < stats->interval.latency_low)) {
898 stats->
interval.latency_total += lint;
903 fr_pcap_t *in,
struct timeval *now,
bool live)
908 memset(&update, 0,
sizeof(update));
914 switch (conf->
stats.out) {
926 #ifdef HAVE_COLLECTDC_H
927 case RS_STATS_OUT_COLLECTD:
936 now->tv_sec += conf->
stats.interval;
940 INFO(
"Muting stats for the next %i milliseconds (warmup)", conf->
stats.timeout);
945 ERROR(
"Failed inserting stats event");
967 for (i = 0; i < num; i++) {
1008 if (request->
event) {
1024 uint64_t count = request->
id;
1034 talloc_free(request);
1062 if ((request->
in->type == PCAP_INTERFACE_IN) && request->
logged) {
1084 talloc_free(request);
1090 request->
event = NULL;
1103 uint8_t
const *
data)
1105 if (!event->
out)
return 0;
1147 pcap_dump((
void *)event->
out->dumper, header, data);
1153 uint8_t
const *
data)
1155 if (!event->
out)
return 0;
1167 if (!(request->
capture_p->
header = talloc(request,
struct pcap_pkthdr)))
return -1;
1168 if (!(request->
capture_p->
data = talloc_array(request, uint8_t, header->caplen))) {
1172 memcpy(request->
capture_p->
header, header,
sizeof(
struct pcap_pkthdr));
1184 pcap_dump((
void *)event->
out->dumper, header, data);
1190 #define RS_CLEANUP_NOW(_x, _s)\
1192 _x->silent_cleanup = _s;\
1193 _x->when = header->ts;\
1194 rs_packet_cleanup(_x);\
1201 struct timeval elapsed = {0, 0};
1202 struct timeval latency;
1208 uint8_t
const *p =
data;
1217 static uint64_t captured = 0;
1225 memset(&search, 0,
sizeof(search));
1227 if (!start_pcap.tv_sec) {
1228 start_pcap = header->ts;
1237 REDEBUG(
"Failed determining link layer header offset");
1242 version = (p[0] & 0xf0) >> 4;
1246 len = (0x0f & ip->ip_vhl) * 4;
1257 REDEBUG(
"IP version invalid %i", version);
1265 if ((
size_t) len > header->caplen) {
1266 REDEBUG(
"Packet too small, we require at least %zu bytes, captured %i bytes",
1267 (
size_t) len, header->caplen);
1279 udp_len = ntohs(udp->len);
1280 diff = udp_len - (header->caplen - (p -
data));
1283 REDEBUG(
"Packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
1297 else if (diff < 0) {
1298 REDEBUG(
"Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
1299 diff * -1, udp_len);
1307 expected =
fr_udp_checksum((uint8_t
const *) udp, ntohs(udp->len), udp->checksum,
1308 ip->ip_src, ip->ip_dst);
1309 if (udp->checksum != expected) {
1310 REDEBUG(
"UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
1311 ntohs(udp->checksum), ntohs(expected));
1324 REDEBUG(
"Failed allocating memory to hold decoded packet");
1331 memcpy(¤t->
data, &p,
sizeof(current->
data));
1352 current->
src_port = ntohs(udp->src);
1353 current->
dst_port = ntohs(udp->dst);
1365 switch (current->
code) {
1385 RDEBUG2(
"Response dropped by filter");
1448 original->
linked = talloc_steal(original, current);
1451 &original->
event)) {
1452 REDEBUG(
"Failed inserting new event");
1457 talloc_free(original);
1471 RDEBUG2(
"Original request dropped by filter");
1496 RDEBUG2(
"Request dropped by filter");
1528 REDEBUG(
"Failed allocating memory to hold expected reply");
1540 ERROR(
"Failed extracting RTX linking pairs from request");
1560 if (tuple && (original != tuple)) {
1609 original->
packet = talloc_steal(original, current);
1617 original->
expect = talloc_steal(original, search.
expect);
1628 original->
id = count;
1629 original->
in =
event->in;
1635 original->
packet = talloc_steal(original, current);
1636 original->
expect = talloc_steal(original, search.
expect);
1683 REDEBUG(
"Failed inserting new event");
1685 talloc_free(original);
1700 rs_tv_sub(&header->ts, &start_pcap, &elapsed);
1710 if (original && original->
linked) {
1732 original->
packet, &elapsed, NULL,
false,
true);
1733 rs_tv_sub(&header->ts, &start_pcap, &elapsed);
1739 &elapsed, &latency, response,
true);
1748 current, &elapsed, NULL, response,
true);
1757 if (response && !original) {
1765 if ((conf->
limit > 0) && (captured >= conf->
limit)) {
1766 INFO(
"Captured %" PRIu64
" packets, exiting...", captured);
1773 static uint64_t count = 0;
1775 pcap_t *handle =
event->
in->handle;
1779 const uint8_t *
data;
1780 struct pcap_pkthdr *header;
1785 if ((event->in->type == PCAP_FILE_IN) || (event->in->type == PCAP_STDIO_IN)) {
1786 bool stats_started =
false;
1791 ret = pcap_next_ex(handle, &header, &data);
1797 DEBUG(
"Done reading packets (%s)", event->in->name);
1808 ERROR(
"Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
1816 if (conf->
stats.interval && !stats_started) {
1818 stats_started =
true;
1836 ret = pcap_next_ex(handle, &header, &data);
1842 ERROR(
"Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
1853 if (wake && ((wake->tv_sec != 0) || (wake->tv_usec >= 100000))) {
1854 DEBUG2(
"Waking up in %d.%01u seconds.", (
int) wake->tv_sec, (
unsigned int) wake->tv_usec / 100000);
1873 if (rcode != 0)
return rcode;
1876 if (rcode != 0)
return rcode;
1879 if (rcode != 0)
return rcode;
1882 if (rcode != 0)
return rcode;
1893 while ((tok =
strsep(&p,
"\t ,")) != NULL) {
1895 if ((*tok ==
'\t') || (*tok ==
' ') || (*tok ==
'\0')) {
1900 ERROR(
"Too many attributes, maximum allowed is %zu", len);
1906 ERROR(
"Error parsing attribute name \"%s\"", tok);
1935 ERROR(
"Empty RADIUS filter '%s'", filter);
1947 vp->vp_strvalue = vp->xlat;
1948 vp->vp_length = talloc_array_length(vp->vp_strvalue) - 1;
1966 while ((tok =
strsep(&p,
"\t ,")) != NULL) {
1969 if ((*tok ==
'\t') || (*tok ==
' ') || (*tok ==
'\0')) {
1975 ERROR(
"Invalid flag \"%s\"", tok);
1992 this->in_request_tree =
false;
2002 this->in_link_tree =
false;
2005 #ifdef HAVE_COLLECTDC_H
2009 static void rs_collectd_reopen(
void *ctx,
struct timeval *now)
2013 struct timeval when;
2015 if (rs_stats_collectd_open(conf) == 0) {
2016 DEBUG2(
"Stats output socket (re)opened");
2023 if (!
fr_event_insert(list, rs_collectd_reopen, list, &when, &event)) {
2024 ERROR(
"Failed inserting re-open event");
2036 if (write(self_pipe[1], &sig,
sizeof(sig)) < 0) {
2037 ERROR(
"Failed writing signal %s to pipe: %s", strsignal(sig),
fr_syserror(errno));
2046 #ifndef HAVE_COLLECTDC_H
2054 ret = read(fd, &sig,
sizeof(sig));
2060 if (ret !=
sizeof(sig)) {
2061 ERROR(
"Failed reading signal from pipe: "
2062 "Expected signal to be %zu bytes but only read %zu byes",
sizeof(sig), ret);
2067 #ifdef HAVE_COLLECTDC_H
2072 gettimeofday(&now, NULL);
2073 rs_collectd_reopen(list, &now);
2081 DEBUG2(
"Signalling event loop to exit");
2086 ERROR(
"Unhandled signal %s", strsignal(sig));
2093 FILE *output = status ? stderr : stdout;
2094 fprintf(output,
"Usage: radsniff [options][stats options] -- [pcap files]\n");
2095 fprintf(output,
"options:\n");
2096 fprintf(output,
" -a List all interfaces available for capture.\n");
2097 fprintf(output,
" -c <count> Number of packets to capture.\n");
2098 fprintf(output,
" -C Enable UDP checksum validation.\n");
2099 fprintf(output,
" -d <directory> Set dictionary directory.\n");
2100 fprintf(output,
" -d <raddb> Set configuration directory (defaults to " RADDBDIR
").\n");
2101 fprintf(output,
" -D <dictdir> Set main dictionary directory (defaults to " DICTDIR
").\n");
2102 fprintf(output,
" -e <event>[,<event>] Only log requests with these event flags.\n");
2103 fprintf(output,
" Event may be one of the following:\n");
2104 fprintf(output,
" - received - a request or response.\n");
2105 fprintf(output,
" - norsp - seen for a request.\n");
2106 fprintf(output,
" - rtx - of a request that we've seen before.\n");
2107 fprintf(output,
" - noreq - could be matched with the response.\n");
2108 fprintf(output,
" - reused - ID too soon.\n");
2109 fprintf(output,
" - error - decoding the packet.\n");
2110 fprintf(output,
" -f <filter> PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
2111 fprintf(output,
" -h This help message.\n");
2112 fprintf(output,
" -i <interface> Capture packets from interface (defaults to all if supported).\n");
2113 fprintf(output,
" -I <file> Read packets from file (overrides input of -F).\n");
2114 fprintf(output,
" -l <attr>[,<attr>] Output packet sig and a list of attributes.\n");
2115 fprintf(output,
" -L <attr>[,<attr>] Detect retransmissions using these attributes to link requests.\n");
2116 fprintf(output,
" -m Don't put interface(s) into promiscuous mode.\n");
2117 fprintf(output,
" -p <port> Filter packets by port (default is 1812).\n");
2118 fprintf(output,
" -P <pidfile> Daemonize and write out <pidfile>.\n");
2119 fprintf(output,
" -q Print less debugging information.\n");
2120 fprintf(output,
" -r <filter> RADIUS attribute request filter.\n");
2121 fprintf(output,
" -R <filter> RADIUS attribute response filter.\n");
2122 fprintf(output,
" -s <secret> RADIUS secret.\n");
2123 fprintf(output,
" -S Write PCAP data to stdout.\n");
2124 fprintf(output,
" -v Show program version information.\n");
2125 fprintf(output,
" -w <file> Write output packets to file.\n");
2126 fprintf(output,
" -x Print more debugging information.\n");
2127 fprintf(output,
"stats options:\n");
2128 fprintf(output,
" -W <interval> Periodically write out statistics every <interval> seconds.\n");
2129 fprintf(output,
" -E Print stats in CSV format.\n");
2130 fprintf(output,
" -T <timeout> How many milliseconds before the request is counted as lost "
2132 #ifdef HAVE_COLLECTDC_H
2133 fprintf(output,
" -N <prefix> The instance name passed to the collectd plugin.\n");
2134 fprintf(output,
" -O <server> Write statistics to this collectd server.\n");
2141 fr_pcap_t *in = NULL, *in_p;
2142 fr_pcap_t **in_head = ∈
2143 fr_pcap_t *out = NULL;
2147 char errbuf[PCAP_ERRBUF_SIZE];
2172 talloc_set_log_stderr();
2174 conf = talloc_zero(NULL,
rs_t);
2182 #ifdef HAVE_TALLOC_SET_MEMLIMIT
2195 #ifdef HAVE_COLLECTDC_H
2201 #ifdef HAVE_COLLECTDC_H
2208 while ((opt = getopt(argc, argv,
"ab:c:Cd:D:e:EFf:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
2212 pcap_if_t *all_devices = NULL;
2215 if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2216 ERROR(
"Error getting available capture devices: %s", errbuf);
2221 for (dev_p = all_devices;
2223 dev_p = dev_p->next) {
2224 INFO(
"%i.%s", i++, dev_p->name);
2234 ERROR(
"Invalid buffer length \"%s\"", optarg);
2240 conf->
limit = atoi(optarg);
2241 if (conf->
limit == 0) {
2242 ERROR(
"Invalid number of packets \"%s\"", optarg);
2253 radius_dir = optarg;
2278 *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
2279 if (!*in_head)
goto finish;
2280 in_head = &(*in_head)->next;
2285 *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
2289 in_head = &(*in_head)->next;
2306 port = atoi(optarg);
2337 #ifdef HAVE_COLLECTDC_H
2338 INFO(
"%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
2339 lcc_version_string());
2341 INFO(
"%s %s", radsniff_version, pcap_lib_version());
2346 out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
2348 ERROR(
"Failed creating pcap file \"%s\"", optarg);
2360 conf->
stats.interval = atoi(optarg);
2362 if (conf->
stats.interval <= 0) {
2363 ERROR(
"Stats interval must be > 0");
2369 conf->
stats.timeout = atoi(optarg);
2370 if (conf->
stats.timeout <= 0) {
2371 ERROR(
"Timeout value must be > 0");
2376 #ifdef HAVE_COLLECTDC_H
2378 conf->
stats.prefix = optarg;
2382 conf->
stats.collectd = optarg;
2383 conf->
stats.out = RS_STATS_OUT_COLLECTD;
2400 while (optind < argc) {
2401 *in_head = fr_pcap_init(conf, argv[optind], PCAP_FILE_IN);
2405 in_head = &(*in_head)->next;
2411 if (!isatty(fileno(stdin))) {
2441 out = fr_pcap_init(conf,
"stdout", PCAP_STDIO_OUT);
2448 *in_head = fr_pcap_init(conf,
"stdin", PCAP_STDIO_IN);
2452 in_head = &(*in_head)->next;
2456 if (conf->
stats.interval && !conf->
stats.out) {
2460 if (conf->
stats.timeout == 0) {
2478 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
2480 ERROR(
"PCAP streams not supported");
2486 snprintf(buffer,
sizeof(buffer),
"udp port %d or %d or %d",
2487 port, port + 1, 3799);
2523 ERROR(
"Failed creating RTX tree");
2566 DEBUG(
"Logging all events");
2586 if (!request_tree) {
2587 ERROR(
"Failed creating request tree");
2595 pcap_if_t *all_devices;
2598 if (pcap_findalldevs(&all_devices, errbuf) < 0) {
2599 ERROR(
"Error getting available capture devices: %s", errbuf);
2604 ERROR(
"No capture files specified and no live interfaces available");
2609 for (dev_p = all_devices;
2611 dev_p = dev_p->next) {
2615 if (!strcmp(dev_p->name,
"any"))
continue;
2617 link_layer = fr_pcap_if_link_layer(errbuf, dev_p);
2618 if (link_layer < 0) {
2619 DEBUG2(
"Skipping %s: %s", dev_p->name, errbuf);
2624 DEBUG2(
"Skipping %s: datalink type %s not supported",
2625 dev_p->name, pcap_datalink_val_to_name(link_layer));
2629 *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
2630 in_head = &(*in_head)->next;
2634 INFO(
"Defaulting to capture on all interfaces");
2641 DEBUG2(
"Sniffing with options:");
2643 char *buff = fr_pcap_device_names(conf, in,
' ');
2644 DEBUG2(
" Device(s) : [%s]", buff);
2648 DEBUG2(
" Writing to : [%s]", out->name);
2650 if (conf->
limit > 0) {
2651 DEBUG2(
" Capture limit (packets) : [%" PRIu64
"]", conf->
limit);
2661 DEBUG2(
" RADIUS request filter :");
2670 DEBUG2(
" RADIUS response filter :");
2678 #ifdef HAVE_COLLECTDC_H
2679 if (conf->
stats.out == RS_STATS_OUT_COLLECTD) {
2681 rs_stats_tmpl_t *tmpl, **next;
2683 if (rs_stats_collectd_open(conf) < 0) {
2687 next = &conf->
stats.tmpl;
2689 for (i = 0; i < (
sizeof(
rs_useful_codes) /
sizeof(*rs_useful_codes)); i++) {
2690 tmpl = rs_stats_collectd_init_latency(conf, next, conf,
"exchanged",
2691 &(stats->
exchange[rs_useful_codes[i]]),
2692 rs_useful_codes[i]);
2694 ERROR(
"Error allocating memory for stats template");
2697 next = &(tmpl->next);
2707 fr_pcap_t **tmp_p = &tmp;
2711 in_p = in_p->next) {
2714 if (fr_pcap_open(in_p) < 0) {
2716 if (conf->
from_auto || (in_p->type == PCAP_FILE_IN)) {
2724 ERROR(
"Failed opening pcap handle (%s): Datalink type %s not supported",
2725 in_p->name, pcap_datalink_val_to_name(in_p->link_layer));
2730 if (fr_pcap_apply_filter(in_p, conf->
pcap_filter) < 0) {
2731 ERROR(
"Failed applying filter");
2737 tmp_p = &(in_p->next);
2743 ERROR(
"No PCAP sources available");
2755 out->link_layer = -1;
2759 in_p = in_p->next) {
2760 if (out->link_layer < 0) {
2761 out->link_layer = in_p->link_layer;
2765 if (out->link_layer != in_p->link_layer) {
2766 ERROR(
"Asked to write to output file, but inputs do not have the same link type");
2774 if (fr_pcap_open(out) < 0) {
2797 if (pipe(self_pipe) < 0) {
2812 in_p = in_p->next) {
2819 event->stats =
stats;
2822 ERROR(
"Failed inserting file descriptor");
2827 buff = fr_pcap_device_names(conf, in,
' ');
2828 DEBUG(
"Sniffing on (%s)", buff);
2834 gettimeofday(&now, NULL);
2860 DEBUG(
"Done sniffing");
bool logged
Whether any messages regarding this request were logged.
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
rs_stats_t * stats
Stats to process.
bool fr_pair_validate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
Uses fr_pair_cmp to verify all VALUE_PAIRs in list match the filter defined by check.
VALUE_PAIR has a single value.
static char const * dict_dir
int sockfd
Socket this packet was read from.
bool decode_attrs
Whether we should decode attributes in the request and response.
void fr_pair_list_fprint(FILE *, VALUE_PAIR const *vp)
Print a list of attributes and enumv.
static int rs_useful_codes[]
fr_dict_attr_t const * link_da[RS_MAX_ATTRS]
fr_dict_attr_ts to link on.
int id
Packet ID (used to link requests/responses).
struct timeval timestamp
When we received the packet.
int fr_radius_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
Calculate/check digest, and decode radius attributes.
bool to_stdout
Were writing pcap data to stdout.
#define RIDEBUG_ENABLED()
int link_da_num
Number of rtx fr_dict_attr_ts.
fr_pcap_t * in
Linked list of PCAP handles to check for drops.
static void rs_packet_cleanup(rs_request_t *request)
static int rs_install_stats_processor(rs_stats_t *stats, fr_event_list_t *el, fr_pcap_t *in, struct timeval *now, bool live)
bool daemonize
Daemonize and write PID out to file.
void fr_radius_print_hex(RADIUS_PACKET *packet)
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.
static char const * radius_dir
Path to raddb directory.
#define RS_DEFAULT_SECRET
Default secret.
static int rs_build_filter(VALUE_PAIR **out, char const *filter)
RFC2865 - Access-Challenge.
int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
bool fr_link_layer_supported(int link_layer)
Check whether fr_link_layer_offset can process a link_layer.
VALUE_PAIR * link_vps
VALUE_PAIRs used to link retransmissions.
bool rbtree_deletebydata(rbtree_t *tree, void const *data)
Delete a node from the tree, based on given data, which MUST have come from rbtree_finddata().
char const * radius_secret
Secret to decode encrypted attributes.
int fr_dict_read(fr_dict_t *dict, char const *dir, char const *filename)
static fr_event_list_t * el
VALUE_PAIR * fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag)
Iterate over a collection of VALUE_PAIRs of a given type in the pairlist.
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
char const * fr_packet_codes[FR_MAX_PACKET_CODE]
rs_latency_t * stats_req
Latency entry for the request type.
fr_ipaddr_t src_ipaddr
Src IP address of packet.
static struct timeval start_pcap
bool verify_udp_checksum
Check UDP checksum in packets.
char * pcap_filter
PCAP filter string applied to live capture devices.
ssize_t fr_link_layer_offset(uint8_t const *data, size_t len, int link_layer)
Returns the length of the link layer header.
fr_event_t * event
Event created when we received the original request.
char const * pidfile
File to write PID to.
int main(int argc, char *argv[])
static void rs_signal_self(int sig)
Write the last signal to the signal pipe.
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Wrapper for RADIUS_PACKET.
void fr_pair_steal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
Steal one VP.
fr_pcap_t * in
PCAP handle the original request was received on.
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
static void rs_got_packet(fr_event_list_t *el, int fd, void *ctx)
#define RS_SOCKET_REOPEN_DELAY
How long we delay re-opening a collectd socket.
static void _unmark_request(void *request)
Callback for when the request is removed from the request tree.
static ssize_t rs_stats_print_code_csv(char *out, size_t outlen, rs_latency_t *stats)
rs_capture_t * capture_p
Next packet slot.
static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
valuepair value must be xlat expanded when it's added to VALUE_PAIR tree.
void * rbtree_finddata(rbtree_t *tree, void const *data)
Find the user data.
#define RS_DEFAULT_PREFIX
Default instance.
char * link_attributes
Names of fr_dict_attr_ts to use for rtx.
uint8_t * data
Packet data (body).
RFC3575/RFC5176 - Disconnect-Ack (positive)
fr_event_list_t * fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status)
uint16_t dst_port
DST Port of packet.
uint16_t src_port
Src port of packet.
fr_ipaddr_t dst_ipaddr
Dst IP address of packet.
bool from_stdin
Were reading pcap data from stdin.
static char const * radsniff_version
int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, fr_event_fd_handler_t handler, void *ctx)
bool promiscuous
Capture in promiscuous mode.
struct rs_latency::@8 interval
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
rs_latency_t exchange[PW_CODE_MAX]
We end up allocating ~16K, but memory is cheap so what the hell.
bool from_dev
Were reading pcap data from devices.
static fr_event_list_t * events
RFC2866 - Accounting-Response.
int fr_fault_setup(char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
RFC3575/RFC5176 - CoA-Ack (positive)
Vendors and attribute names.
static void rs_stats_print_csv(rs_update_t *this, rs_stats_t *stats, UNUSED struct timeval *now)
static void rs_stats_process(void *ctx, struct timeval *now)
Process stats for a single interval.
#define RS_DEFAULT_TIMEOUT
Standard timeout of 5s + 300ms to cover network latency.
static void rs_tv_sub(struct timeval const *end, struct timeval const *start, struct timeval *elapsed)
RFC2865 - Access-Request.
RADIUS_PACKET * packet
The original packet.
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
RADIUS_PACKET * linked
The subsequent response or forwarded request the packet was linked against.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
uint64_t latency_smoothed_count
Number of CMA datapoints processed.
uint8_t * data
PCAP packet data.
static int rs_build_event_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
static void rs_daemonize(char const *pidfile)
Fork and kill the parent process, writing out our PID.
uint64_t id
Monotonically increasing packet counter.
rbtree_t * rbtree_create(TALLOC_CTX *ctx, rb_comparator_t compare, rb_free_t node_free, int flags)
Create a new RED-BLACK tree.
fr_pcap_t * in
PCAP handle event occurred on.
PW_CODE filter_request_code
Filter request packets by code.
bool fr_radius_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
See if the data pointed to by PTR is a valid RADIUS packet.
rs_stats_print_header_cb_t head
Print header.
rs_latency_t * stats_rsp
Latency entry for the request type.
static int rs_response_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header, uint8_t const *data)
int(* rbcmp)(void const *, void const *)
bool in_link_tree
Whether the request is currently in the linked tree.
static int rs_packet_cmp(rs_request_t const *a, rs_request_t const *b)
Wrapper around fr_packet_cmp to strip off the outer request struct.
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
static rbtree_t * request_tree
int buffer_pkts
Size of the ring buffer to setup for live capture.
RFC2866 - Accounting-Request.
RADIUS_PACKET * fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new RADIUS_PACKET.
void void fr_perror(char const *,...) CC_HINT(format(printf
static int _request_free(rs_request_t *request)
union fr_ipaddr_t::@1 ipaddr
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
unsigned int code
Packet code (type).
static void rs_packet_print_csv_header(void)
fr_dict_attr_t const * list_da[RS_MAX_ATTRS]
Output CSV with these attribute values.
static void _rs_event(void *ctx, UNUSED struct timeval *now)
#define RADIUS_DICTIONARY
FD data which gets passed to callbacks.
static void rs_stats_process_latency(rs_latency_t *stats)
Update smoothed average.
Stores an attribute, a value and various bits of other data.
int8_t fr_pair_cmp_by_da_tag(void const *a, void const *b)
static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet, UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response, bool body)
static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result)
static int rs_request_to_pcap(rs_event_t *event, rs_request_t *request, struct pcap_pkthdr const *header, uint8_t const *data)
bool silent_cleanup
Cleanup was forced before normal expiry period, ignore stats about packet loss.
char const * filter_response
Raw response filter string.
RFC3575/RFC5176 - CoA-Request.
void fr_quick_sort(void const *to_sort[], int min_idx, int max_idx, fr_cmp_t cmp)
Quick sort an array of pointers using a comparator.
uint8_t vector[AUTH_VECTOR_LEN]
RADIUS authentication vector.
char const * fr_strerror(void)
Get the last library error.
struct pcap_pkthdr * header
PCAP packet header.
int fr_event_run(fr_event_list_t *el, struct timeval *when)
rs_stats_print_cb_t body
Print body.
static void rs_stats_process_counters(rs_latency_t *stats)
static void rs_signal_action(UNUSED fr_event_list_t *list, int fd, UNUSED void *ctx)
Read the last signal from the signal pipe.
PW_CODE filter_response_code
Filter response packets by code.
static int rs_check_pcap_drop(fr_pcap_t *in)
Query libpcap to see if it dropped any packets.
VALUE_PAIR * fr_pair_find_by_da(VALUE_PAIR *head, fr_dict_attr_t const *da, int8_t tag)
Find the pair with the matching DAs.
static void rs_packet_print(rs_request_t *request, uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet, struct timeval *elapsed, struct timeval *latency, bool response, bool body)
rs_status_t event_flags
Events we log and capture on.
static void rs_stats_print_code_fancy(rs_latency_t *stats, PW_CODE code)
uint64_t rt_rsp
Number of times we saw a retransmitted response packet.
struct radius_packet_t radius_packet_t
bool to_file
Were writing pcap data to files.
char name[1]
Attribute name.
VALUE_PAIR * filter_response_vps
Sorted filter vps.
#define RS_RETRANSMIT_MAX
Maximum number of times we expect to see a packet retransmitted.
char * list_attributes
Raw attribute filter string.
RADIUS_PACKET * expect
Request/response.
struct timeval quiet
We may need to 'mute' the stats if libpcap starts dropping packets, or we run out of memory...
rs_capture_t capture[RS_RETRANSMIT_MAX]
Buffered request packets (if a response filter has been applied).
bool print_packet
Print packet info, disabled with -W.
VALUE_PAIR * filter_request_vps
Sorted filter vps.
value_type_t type
Type of pointer in value union.
void fr_pair_list_sort(VALUE_PAIR **vps, fr_cmp_t cmp)
Sort a linked list of VALUE_PAIRs using merge sort.
static int self_pipe[2]
Signals from sig handlers.
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
static void rs_stats_print_csv_header(rs_update_t *this)
void fr_radius_free(RADIUS_PACKET **)
Free a RADIUS_PACKET.
RADIUS_PACKET * fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *)
Allocate a new RADIUS_PACKET response.
rs_packet_logger_t logger
Packet logger.
struct timeval when
Time when the packet was received, or next time an event is scheduled.
uint64_t limit
Maximum number of packets to capture.
int fr_event_list_num_fds(fr_event_list_t *el)
size_t data_len
Length of packet data.
bool rbtree_insert(rbtree_t *tree, void *data)
static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
Update latency statistics for request/response and forwarded packets.
int fr_event_loop(fr_event_list_t *el)
RFC2865/RFC5997 - Status Server (response)
static size_t rs_snprint_csv(char *out, size_t outlen, char const *in, size_t inlen)
fr_pcap_t * out
Where to write output.
char const * filter_request
Raw request filter string.
VALUE_PAIR * fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
Copy a single valuepair.
void int fr_set_signal(int sig, sig_t func)
Sets a signal handler using sigaction if available, else signal.
static int rs_rtx_cmp(rs_request_t const *a, rs_request_t const *b)
Compare requests using packet info and lists of attributes.
fr_event_list_t * list
List to insert new event into.
int fr_event_delete(fr_event_list_t *el, fr_event_t **parent)
VALUE_PAIR * fr_cursor_remove(vp_cursor_t *cursor)
Remove the current pair.
RFC2865/RFC5997 - Status Server (request)
double latency_smoothed
Smoothed moving average.
size_t strlcpy(char *dst, char const *src, size_t siz)
PW_CODE
RADIUS packet codes.
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
static void rs_time_print(char *out, size_t len, struct timeval const *t)
#define RS_FORCE_YIELD
Service another descriptor every X number of packets.
String of printable characters.
void fr_event_loop_exit(fr_event_list_t *el, int code)
int intervals
Number of stats intervals.
FR_TOKEN fr_pair_list_afrom_str(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **head)
Read one line of attribute/value pairs into a list.
Statistic write/print event.
int fr_event_insert(fr_event_list_t *el, fr_event_callback_t callback, void *ctx, struct timeval *when, fr_event_t **parent)
bool from_auto
From list was auto-generated.
char * strsep(char **stringp, char const *delim)
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
VALUE_PAIR * fr_cursor_next_by_da(vp_cursor_t *cursor, fr_dict_attr_t const *da, int8_t tag) CC_HINT(nonnull)
Iterate over attributes of a given DA in the pairlist.
static const FR_NAME_NUMBER rs_events[]
bool fr_event_loop_exiting(fr_event_list_t *el)
bool in_request_tree
Whether the request is currently in the request tree.
uint64_t rt_req
Number of times we saw the same request packet.
static void NEVER_RETURNS usage(int status)
Stats for a single interval.
int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
static void rs_stats_print_fancy(rs_update_t *this, rs_stats_t *stats, struct timeval *now)
static int rs_build_dict_list(fr_dict_attr_t const **out, size_t len, char *list)
bool from_file
Were reading pcap data from files.
size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
Convert binary data to a hex string.
#define is_radius_code(_x)
#define RS_CLEANUP_NOW(_x, _s)
int fr_dict_init(TALLOC_CTX *ctx, fr_dict_t **out, char const *dir, char const *fn, char const *name)
(re)initialize a protocol dictionary
static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet, struct timeval *elapsed, struct timeval *latency, bool response, bool body)
static void _rs_event_status(struct timeval *wake)
static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, fr_dict_attr_t const *da[], int num)
Copy a subset of attributes from one list into the other.
static rbtree_t * link_tree
#define RADIUSD_MAGIC_NUMBER
static void _unmark_link(void *request)
Callback for when the request is removed from the link tree.
int fr_pair_list_cmp(VALUE_PAIR *a, VALUE_PAIR *b)
Determine equality of two lists.
RFC3575/RFC5176 - Disconnect-Request.
int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
fr_event_list_t * list
The event list.
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_t *dict, char const *attr)
Locate a fr_dict_attr_t by its name.