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>
24 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/libradius.h>
32 #include <freeradius-devel/radpaths.h>
33 #include <freeradius-devel/conf.h>
34 #include <freeradius-devel/radsniff.h>
36 static const char *radius_secret = "testing123";
37 static VALUE_PAIR *filter_vps = NULL;
39 #define DEBUG if (fr_debug_flag) printf
41 static int minimal = 0;
42 static int do_sort = 0;
43 struct timeval start_pcap = {0, 0};
44 static rbtree_t *filter_tree = NULL;
45 static rbtree_t *request_tree = NULL;
46 static pcap_dumper_t *pcap_dumper = NULL;
47 static RADIUS_PACKET *nullpacket = NULL;
49 typedef int (*rbcmp)(const void *, const void *);
51 static int filter_packet(RADIUS_PACKET *packet)
53 VALUE_PAIR *check_item;
55 unsigned int pass, fail;
59 for (vp = packet->vps; vp != NULL; vp = vp->next) {
60 for (check_item = filter_vps;
62 check_item = check_item->next)
63 if ((check_item->attribute == vp->attribute)
64 && (check_item->operator != T_OP_SET)) {
65 compare = paircmp(check_item, vp);
74 if (fail == 0 && pass != 0) {
76 * Cache authentication requests, as the replies
77 * may not match the RADIUS filter.
79 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
80 (packet->code == PW_ACCOUNTING_REQUEST)) {
81 rbtree_deletebydata(filter_tree, packet);
83 if (!rbtree_insert(filter_tree, packet)) {
85 fprintf(stderr, "radsniff: Out of memory\n");
89 return 0; /* matched */
93 * Don't create erroneous matches.
95 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
96 (packet->code == PW_ACCOUNTING_REQUEST)) {
97 rbtree_deletebydata(filter_tree, packet);
102 * Else see if a previous Access-Request
103 * matched. If so, also print out the
104 * matching accept, reject, or challenge.
106 if ((packet->code == PW_AUTHENTICATION_ACK) ||
107 (packet->code == PW_AUTHENTICATION_REJECT) ||
108 (packet->code == PW_ACCESS_CHALLENGE) ||
109 (packet->code == PW_ACCOUNTING_RESPONSE)) {
110 RADIUS_PACKET *reply;
113 * This swaps the various fields.
115 reply = rad_alloc_reply(packet);
116 if (!reply) goto oom;
119 if (rbtree_finddata(filter_tree, reply)) {
133 static void sort(RADIUS_PACKET *packet)
136 VALUE_PAIR *vp, *tmp;
137 VALUE_PAIR *array[1024]; /* way more than necessary */
140 for (vp = packet->vps; vp != NULL; vp = vp->next) {
144 if (size == 0) return;
146 for (i = 0; i < size - 1; i++) {
147 for (j = 0; j < size - 1 - i; j++) {
148 if (array[j + 1]->attribute < array[j]->attribute) {
150 array[j] = array[j + 1];
157 * And put them back again.
159 vp = packet->vps = array[0];
160 for (i = 1; i < size; i++) {
168 static void tv_sub(const struct timeval *end, const struct timeval *start,
169 struct timeval *elapsed)
171 elapsed->tv_sec = end->tv_sec - start->tv_sec;
172 if (elapsed->tv_sec > 0) {
174 elapsed->tv_usec = USEC;
176 elapsed->tv_usec = 0;
178 elapsed->tv_usec += end->tv_usec;
179 elapsed->tv_usec -= start->tv_usec;
181 if (elapsed->tv_usec >= USEC) {
182 elapsed->tv_usec -= USEC;
187 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
189 /* Just a counter of how many packets we've had */
190 static int count = 1;
191 /* Define pointers for packet's attributes */
192 const struct ethernet_header *ethernet; /* The ethernet header */
193 const struct ip_header *ip; /* The IP header */
194 const struct udp_header *udp; /* The UDP header */
195 const uint8_t *payload; /* Packet payload */
196 /* And define the size of the structures we're using */
197 int size_ethernet = sizeof(struct ethernet_header);
198 int size_ip = sizeof(struct ip_header);
199 int size_udp = sizeof(struct udp_header);
201 RADIUS_PACKET *packet, *original;
202 struct timeval elapsed;
204 args = args; /* -Wunused */
206 /* Define our packet's attributes */
208 if ((data[0] == 2) && (data[1] == 0) &&
209 (data[2] == 0) && (data[3] == 0)) {
210 ip = (const struct ip_header*) (data + 4);
213 ethernet = (const struct ethernet_header*)(data);
214 ip = (const struct ip_header*)(data + size_ethernet);
216 udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
217 payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);
219 packet = malloc(sizeof(*packet));
221 fprintf(stderr, "Out of memory\n");
225 memset(packet, 0, sizeof(*packet));
226 packet->src_ipaddr.af = AF_INET;
227 packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
228 packet->src_port = ntohs(udp->udp_sport);
229 packet->dst_ipaddr.af = AF_INET;
230 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
231 packet->dst_port = ntohs(udp->udp_dport);
233 packet->data = payload;
234 packet->data_len = header->len - (payload - data);
236 if (!rad_packet_ok(packet, 0)) {
237 printf("Packet: %s\n", fr_strerror());
239 printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
240 printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
241 printf("\tType: %s\n", fr_packet_codes[packet->code]);
247 switch (packet->code) {
249 /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
250 original = nullpacket;
252 case PW_AUTHENTICATION_ACK:
253 /* look for a matching request and use it for decoding */
254 original = rbtree_finddata(request_tree, packet);
256 case PW_AUTHENTICATION_REQUEST:
257 /* save the request for later matching */
258 original = rad_alloc_reply(packet);
259 if (original) { /* just ignore allocation failures */
260 rbtree_deletebydata(request_tree, original);
261 rbtree_insert(request_tree, original);
265 /* don't attempt to decode any encrypted attributes */
270 * Decode the data without bothering to check the signatures.
272 if (rad_decode(packet, original, radius_secret) != 0) {
278 /* we've seen a successfull reply to this, so delete it now */
280 rbtree_deletebydata(request_tree, original);
282 if (filter_vps && filter_packet(packet)) {
284 DEBUG("Packet number %d doesn't match\n", count++);
289 pcap_dump((void *) pcap_dumper, header, data);
293 printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
295 /* Print the RADIUS packet */
296 printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
297 printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
298 if (fr_debug_flag) printf("\t(%d packets)", count++);
300 if (!start_pcap.tv_sec) {
301 start_pcap = header->ts;
304 tv_sub(&header->ts, &start_pcap, &elapsed);
306 printf("\t+%u.%03u", (unsigned int) elapsed.tv_sec,
307 (unsigned int) elapsed.tv_usec / 1000);
308 if (!minimal) printf("\n");
309 if (!minimal && packet->vps) {
310 if (do_sort) sort(packet);
312 vp_printlist(stdout, packet->vps);
313 pairfree(&packet->vps);
316 if (fr_debug_flag > 2) rad_print_hex(packet);
321 * If we're doing filtering, Access-Requests are cached
322 * in the filter tree.
325 ((packet->code != PW_AUTHENTICATION_REQUEST) &&
326 (packet->code != PW_ACCOUNTING_REQUEST))) {
331 static void NEVER_RETURNS usage(int status)
333 FILE *output = status ? stderr : stdout;
334 fprintf(output, "usage: radsniff [options]\n");
335 fprintf(output, "options:\n");
336 fprintf(output, "\t-c count\tNumber of packets to capture.\n");
337 fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
338 fprintf(output, "\t-F\t\tFilter PCAP file from stdin to stdout.\n");
339 fprintf(output, "\t\t\tOutput file will contain RADIUS packets.\n");
340 fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813)\n");
341 fprintf(output, "\t-h\t\tPrint this help message.\n");
342 fprintf(output, "\t-i interface\tInterface to capture.\n");
343 fprintf(output, "\t-I filename\tRead packets from filename.\n");
344 fprintf(output, "\t-m\t\tPrint packet headers only, not contents.\n");
345 fprintf(output, "\t-p port\t\tListen for packets on port.\n");
346 fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
347 fprintf(output, "\t-s secret\tRADIUS secret.\n");
348 fprintf(output, "\t-S\t\tSort attributes in the packet.\n");
349 fprintf(output, "\t\t\tUsed to compare server results.\n");
350 fprintf(output, "\t-w file\tWrite output packets to file.\n");
351 fprintf(output, "\t-x\t\tPrint out debugging information.\n");
355 int main(int argc, char *argv[])
357 char *dev; /* sniffing device */
358 char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
359 pcap_t *descr; /* sniff handler */
360 struct bpf_program fp; /* hold compiled program */
361 bpf_u_int32 maskp; /* subnet mask */
362 bpf_u_int32 netp; /* ip */
364 char *pcap_filter = NULL;
365 char *radius_filter = NULL;
366 char *filename = NULL;
367 int printable_output = 1;
368 char *dump_file = NULL;
369 int packet_count = -1; /* how many packets to sniff */
372 const char *radius_dir = RADIUS_DIR;
374 int filter_stdin = 0;
377 dev = pcap_lookupdev(errbuf);
380 while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:mp:r:s:Sw:xX")) != EOF) {
384 packet_count = atoi(optarg);
385 if (packet_count <= 0) {
386 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
395 printable_output = 0;
398 pcap_filter = optarg;
416 radius_filter = optarg;
419 radius_secret = optarg;
426 printable_output = 0;
429 case 'X': /* for backwards compatibility */
438 * Cross-check command-line arguments.
440 if (filter_stdin && (filename || dump_file)) usage(1);
442 #ifndef HAVE_PCAP_FOPEN_OFFLINE
444 fr_perror("-F is unsupported on this platform.");
450 pcap_filter = buffer;
451 snprintf(buffer, sizeof(buffer), "udp port %d or %d",
456 * There are times when we don't need the dictionaries.
458 if (printable_output) {
459 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
460 fr_perror("radsniff");
466 parsecode = userparse(radius_filter, &filter_vps);
467 if (parsecode == T_OP_INVALID) {
468 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror());
472 fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
476 filter_tree = rbtree_create((rbcmp) fr_packet_cmp,
479 fprintf(stderr, "radsniff: Failed creating filter tree\n");
484 /* setup the request tree */
485 request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0);
487 fprintf(stderr, "radsniff: Failed creating request tree\n");
491 /* allocate a null packet for decrypting attributes in CoA requests */
492 nullpacket = rad_alloc(0);
494 fprintf(stderr, "radsniff: Out of memory\n");
499 pcap_lookupnet(dev, &netp, &maskp, errbuf);
501 /* Print device to the user */
503 if (dev) printf("Device: [%s]\n", dev);
504 if (packet_count > 0) {
505 printf("Num of packets: [%d]\n",
508 printf("PCAP filter: [%s]\n", pcap_filter);
510 printf("RADIUS filter:\n");
511 vp_printlist(stdout, filter_vps);
513 printf("RADIUS secret: [%s]\n", radius_secret);
516 /* Open the device so we can spy */
518 descr = pcap_open_offline(filename, errbuf);
520 #ifdef HAVE_PCAP_FOPEN_OFFLINE
521 } else if (filter_stdin) {
522 descr = pcap_fopen_offline(stdin, errbuf);
526 fprintf(stderr, "radsniff: No filename or device was specified.\n");
530 descr = pcap_open_live(dev, 65536, 1, 0, errbuf);
534 fprintf(stderr, "radsniff: pcap_open_live failed (%s)\n", errbuf);
539 pcap_dumper = pcap_dump_open(descr, dump_file);
541 fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(descr));
545 #ifdef HAVE_PCAP_DUMP_FOPEN
546 } else if (filter_stdin) {
547 pcap_dumper = pcap_dump_fopen(descr, stdout);
549 fprintf(stderr, "radsniff: Failed opening stdout: %s\n", pcap_geterr(descr));
556 /* Apply the rules */
557 if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
559 fprintf(stderr, "radsniff: pcap_compile failed\n");
562 if (pcap_setfilter(descr, &fp) == -1)
564 fprintf(stderr, "radsniff: pcap_setfilter failed\n");
568 /* Now we can set our callback function */
569 pcap_loop(descr, packet_count, got_packet, NULL);
572 if (filter_tree) rbtree_free(filter_tree);
574 DEBUG("Done sniffing\n");