2 * radsniff.c Display the RADIUS traffic on the network.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2006 The FreeRADIUS server project
21 * Copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
27 #include <freeradius-devel/libradius.h>
31 #include <freeradius-devel/radpaths.h>
32 #include <freeradius-devel/conf.h>
33 #include <freeradius-devel/radsniff.h>
35 static char const *radius_secret = "testing123";
36 static VALUE_PAIR *filter_vps = NULL;
38 static bool do_sort = false;
39 static bool to_stdout = false;
42 #ifndef PCAP_NETMASK_UNKNOWN
43 # define PCAP_NETMASK_UNKNOWN 0
47 #define DEBUG1 if (fr_debug_flag > 2) fprintf
49 #define DEBUG if (fr_debug_flag > 1) fprintf
51 #define INFO if (fr_debug_flag > 0) fprintf
53 struct timeval start_pcap = {0, 0};
54 static rbtree_t *filter_tree = NULL;
55 static rbtree_t *request_tree = NULL;
56 static pcap_dumper_t *out = NULL;
57 static RADIUS_PACKET *nullpacket = NULL;
59 typedef int (*rbcmp)(void const *, void const *);
61 static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
62 #ifdef RADIUSD_VERSION_COMMIT
63 " (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
65 ", built on " __DATE__ " at " __TIME__;
67 static int filter_packet(RADIUS_PACKET *packet)
69 vp_cursor_t cursor, check_cursor;
70 VALUE_PAIR *check_item;
72 unsigned int pass, fail;
76 for (vp = paircursor(&cursor, &packet->vps);
78 vp = pairnext(&cursor)) {
79 for (check_item = paircursor(&check_cursor, &filter_vps);
81 check_item = pairnext(&check_cursor))
82 if ((check_item->da == vp->da)
83 && (check_item->op != T_OP_SET)) {
84 compare = paircmp(check_item, vp);
92 if (fail == 0 && pass != 0) {
94 * Cache authentication requests, as the replies
95 * may not match the RADIUS filter.
97 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
98 (packet->code == PW_ACCOUNTING_REQUEST)) {
99 rbtree_deletebydata(filter_tree, packet);
101 if (!rbtree_insert(filter_tree, packet)) {
103 fprintf(stderr, "radsniff: Out of memory\n");
107 return 0; /* matched */
111 * Don't create erroneous matches.
113 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
114 (packet->code == PW_ACCOUNTING_REQUEST)) {
115 rbtree_deletebydata(filter_tree, packet);
120 * Else see if a previous Access-Request
121 * matched. If so, also print out the
122 * matching accept, reject, or challenge.
124 if ((packet->code == PW_AUTHENTICATION_ACK) ||
125 (packet->code == PW_AUTHENTICATION_REJECT) ||
126 (packet->code == PW_ACCESS_CHALLENGE) ||
127 (packet->code == PW_ACCOUNTING_RESPONSE)) {
128 RADIUS_PACKET *reply;
131 * This swaps the various fields.
133 reply = rad_alloc_reply(NULL, packet);
134 if (!reply) goto oom;
137 if (rbtree_finddata(filter_tree, reply)) {
149 static void tv_sub(struct timeval const *end, struct timeval const *start,
150 struct timeval *elapsed)
152 elapsed->tv_sec = end->tv_sec - start->tv_sec;
153 if (elapsed->tv_sec > 0) {
155 elapsed->tv_usec = USEC;
157 elapsed->tv_usec = 0;
159 elapsed->tv_usec += end->tv_usec;
160 elapsed->tv_usec -= start->tv_usec;
162 if (elapsed->tv_usec >= USEC) {
163 elapsed->tv_usec -= USEC;
168 static void got_packet(UNUSED uint8_t *args, struct pcap_pkthdr const*header, uint8_t const *data)
171 static int count = 1; /* Packets seen */
174 * Define pointers for packet's attributes
176 const struct ip_header *ip; /* The IP header */
177 const struct udp_header *udp; /* The UDP header */
178 const uint8_t *payload; /* Packet payload */
181 * And define the size of the structures we're using
183 int size_ethernet = sizeof(struct ethernet_header);
184 int size_ip = sizeof(struct ip_header);
185 int size_udp = sizeof(struct udp_header);
190 RADIUS_PACKET *packet, *original;
191 struct timeval elapsed;
194 * Define our packet's attributes
197 if ((data[0] == 2) && (data[1] == 0) &&
198 (data[2] == 0) && (data[3] == 0)) {
199 ip = (struct ip_header const *) (data + 4);
202 ip = (struct ip_header const *)(data + size_ethernet);
205 udp = (struct udp_header const *)(((uint8_t const *) ip) + size_ip);
206 payload = (uint8_t const *)(((uint8_t const *) udp) + size_udp);
208 packet = rad_alloc(NULL, 0);
210 fprintf(stderr, "Out of memory\n");
214 packet->src_ipaddr.af = AF_INET;
215 packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
216 packet->src_port = ntohs(udp->udp_sport);
217 packet->dst_ipaddr.af = AF_INET;
218 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
219 packet->dst_port = ntohs(udp->udp_dport);
221 memcpy(&packet->data, &payload, sizeof(packet->data));
222 packet->data_len = header->len - (payload - data);
224 if (!rad_packet_ok(packet, 0)) {
225 DEBUG(log_dst, "Packet: %s\n", fr_strerror());
227 DEBUG(log_dst, " From %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
228 DEBUG(log_dst, " To: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
229 DEBUG(log_dst, " Type: %s\n", fr_packet_codes[packet->code]);
235 switch (packet->code) {
237 /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
238 original = nullpacket;
240 case PW_AUTHENTICATION_ACK:
241 /* look for a matching request and use it for decoding */
242 original = rbtree_finddata(request_tree, packet);
244 case PW_AUTHENTICATION_REQUEST:
245 /* save the request for later matching */
246 original = rad_alloc_reply(NULL, packet);
247 if (original) { /* just ignore allocation failures */
248 rbtree_deletebydata(request_tree, original);
249 rbtree_insert(request_tree, original);
253 /* don't attempt to decode any encrypted attributes */
258 * Decode the data without bothering to check the signatures.
260 if (rad_decode(packet, original, radius_secret) != 0) {
267 * We've seen a successful reply to this, so delete it now
270 rbtree_deletebydata(request_tree, original);
272 if (filter_vps && filter_packet(packet)) {
274 DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
279 pcap_dump((void *) out, header, data);
283 INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);
286 * Print the RADIUS packet
288 INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
289 INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
291 DEBUG1(log_dst, "\t(%d packets)", count++);
293 if (!start_pcap.tv_sec) {
294 start_pcap = header->ts;
297 tv_sub(&header->ts, &start_pcap, &elapsed);
299 INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
300 (unsigned int) elapsed.tv_usec / 1000);
302 if (fr_debug_flag > 1) {
303 DEBUG(log_dst, "\n");
306 pairsort(&packet->vps, true);
308 vp_printlist(log_dst, packet->vps);
309 pairfree(&packet->vps);
315 if (!to_stdout && (fr_debug_flag > 4)) {
316 rad_print_hex(packet);
323 * If we're doing filtering, Access-Requests are cached in the
327 ((packet->code != PW_AUTHENTICATION_REQUEST) &&
328 (packet->code != PW_ACCOUNTING_REQUEST))) {
334 /** Wrapper function to allow rad_free to be called as an rbtree destructor callback
336 * @param packet to free.
338 static void _rb_rad_free(void *packet)
340 rad_free((RADIUS_PACKET **) &packet);
343 static void NEVER_RETURNS usage(int status)
345 FILE *output = status ? stderr : stdout;
346 fprintf(output, "Usage: radsniff [options]\n");
347 fprintf(output, "options:\n");
348 fprintf(output, " -c <count> Number of packets to capture.\n");
349 fprintf(output, " -d <directory> Set dictionary directory.\n");
350 fprintf(output, " -F Filter PCAP file from stdin to stdout.\n");
351 fprintf(output, " -f <filter> PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
352 fprintf(output, " -h This help message.\n");
353 fprintf(output, " -i <interface> Capture packets from interface (defaults to any if supported).\n");
354 fprintf(output, " -I <file> Read packets from file (overrides input of -F).\n");
355 fprintf(output, " -p <port> Filter packets by port (default is 1812).\n");
356 fprintf(output, " -q Print less debugging information.\n");
357 fprintf(output, " -r <filter> RADIUS attribute filter.\n");
358 fprintf(output, " -s <secret> RADIUS secret.\n");
359 fprintf(output, " -S Sort attributes in the packet (useful for diffing responses).\n");
360 fprintf(output, " -v Show program version information.\n");
361 fprintf(output, " -w <file> Write output packets to file (overrides output of -F).\n");
362 fprintf(output, " -x Print more debugging information (defaults to -xx).\n");
366 int main(int argc, char *argv[])
368 char const *from_dev = NULL; /* Capture from device */
369 char const *from_file = NULL; /* Read from pcap file */
370 bool from_stdin = false; /* Read from stdin */
372 pcap_t *in = NULL; /* PCAP input handle */
374 int limit = -1; /* How many packets to sniff */
376 char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */
378 char *to_file = NULL; /* PCAP output file */
380 char *pcap_filter = NULL; /* PCAP filter string */
381 char *radius_filter = NULL;
384 struct bpf_program fp; /* Holds compiled filter */
385 bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN; /* Device Subnet mask */
386 bpf_u_int32 ip_addr = 0; /* Device IP */
392 char const *radius_dir = RADIUS_DIR;
397 talloc_set_log_stderr();
402 while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) {
405 limit = atoi(optarg);
407 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
419 pcap_filter = optarg;
434 if (fr_debug_flag > 0) {
439 radius_filter = optarg;
442 radius_secret = optarg;
448 INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version());
464 * Mismatch between the binary and the libraries it depends on
466 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
467 fr_perror("radsniff");
471 /* What's the point in specifying -F ?! */
472 if (from_stdin && from_file && to_file) {
476 /* Can't read from both... */
477 if (from_file && from_dev) {
481 /* Reading from file overrides stdin */
482 if (from_stdin && (from_file || from_dev)) {
486 /* Writing to file overrides stdout */
487 if (to_file && to_stdout) {
492 * If were writing pcap data stdout we *really* don't want to send
493 * logging there as well.
495 log_dst = to_stdout ? stderr : stdout;
497 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
498 if (from_stdin || to_stdout) {
499 fprintf(stderr, "radsniff: PCAP streams not supported.\n");
505 pcap_filter = buffer;
506 snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
507 port, port + 1, 3799);
511 * There are times when we don't need the dictionaries.
514 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
515 fr_perror("radsniff");
521 parsecode = userparse(NULL, radius_filter, &filter_vps);
522 if (parsecode == T_OP_INVALID) {
523 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror());
528 fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
532 filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
534 fprintf(stderr, "radsniff: Failed creating filter tree\n");
540 * Setup the request tree
542 request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
544 fprintf(stderr, "radsniff: Failed creating request tree\n");
549 * Allocate a null packet for decrypting attributes in CoA requests
551 nullpacket = rad_alloc(NULL, 0);
553 fprintf(stderr, "radsniff: Out of memory\n");
558 * Get the default capture device
560 if (!from_stdin && !from_file && !from_dev) {
561 from_dev = pcap_lookupdev(errbuf);
563 fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf);
567 INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev);
571 * Print captures values which will be used
573 if (fr_debug_flag > 2) {
574 DEBUG1(log_dst, "Sniffing with options:\n");
575 if (from_dev) DEBUG1(log_dst, " Device : [%s]\n", from_dev);
576 if (limit > 0) DEBUG1(log_dst, " Capture limit (packets) : [%d]\n", limit);
577 DEBUG1(log_dst, " PCAP filter : [%s]\n", pcap_filter);
578 DEBUG1(log_dst, " RADIUS secret : [%s]\n", radius_secret);
579 if (filter_vps){DEBUG1(log_dst, " RADIUS filter :\n");
580 vp_printlist(log_dst, filter_vps);
585 * Figure out whether were doing a reading from a file, doing a live
586 * capture or reading from stdin.
589 in = pcap_open_offline(from_file, errbuf);
590 #ifdef HAVE_PCAP_FOPEN_OFFLINE
591 } else if (from_stdin) {
592 in = pcap_fopen_offline(stdin, errbuf);
594 } else if (from_dev) {
595 pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf);
596 in = pcap_open_live(from_dev, 65536, 1, 1, errbuf);
598 fprintf(stderr, "radsniff: No capture devices available\n");
602 fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf);
607 out = pcap_dump_open(in, to_file);
609 fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in));
612 #ifdef HAVE_PCAP_DUMP_FOPEN
613 } else if (to_stdout) {
614 out = pcap_dump_fopen(in, stdout);
616 fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in));
625 if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) {
626 fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in));
630 if (pcap_setfilter(in, &fp) < 0) {
631 fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in));
636 * Enter the main capture loop...
638 pcap_loop(in, limit, got_packet, NULL);
641 * ...were done capturing.
645 pcap_dump_close(out);
649 rbtree_free(filter_tree);
652 INFO(log_dst, "Done sniffing\n");