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 pcap_dumper_t *pcap_dumper = NULL;
47 typedef int (*rbcmp)(const void *, const void *);
49 static int filter_packet(RADIUS_PACKET *packet)
51 VALUE_PAIR *check_item;
53 unsigned int pass, fail;
57 for (vp = packet->vps; vp != NULL; vp = vp->next) {
58 for (check_item = filter_vps;
60 check_item = check_item->next)
61 if ((check_item->attribute == vp->attribute)
62 && (check_item->operator != T_OP_SET)) {
63 compare = paircmp(check_item, vp);
72 if (fail == 0 && pass != 0) {
74 * Cache authentication requests, as the replies
75 * may not match the RADIUS filter.
77 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
78 (packet->code == PW_ACCOUNTING_REQUEST)) {
79 rbtree_deletebydata(filter_tree, packet);
81 if (!rbtree_insert(filter_tree, packet)) {
83 fprintf(stderr, "radsniff: Out of memory\n");
87 return 0; /* matched */
91 * Don't create erroneous matches.
93 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
94 (packet->code == PW_ACCOUNTING_REQUEST)) {
95 rbtree_deletebydata(filter_tree, packet);
100 * Else see if a previous Access-Request
101 * matched. If so, also print out the
102 * matching accept, reject, or challenge.
104 if ((packet->code == PW_AUTHENTICATION_ACK) ||
105 (packet->code == PW_AUTHENTICATION_REJECT) ||
106 (packet->code == PW_ACCESS_CHALLENGE) ||
107 (packet->code == PW_ACCOUNTING_RESPONSE)) {
108 RADIUS_PACKET *reply;
111 * This swaps the various fields.
113 reply = rad_alloc_reply(packet);
114 if (!reply) goto oom;
117 if (rbtree_finddata(filter_tree, reply)) {
131 static void sort(RADIUS_PACKET *packet)
134 VALUE_PAIR *vp, *tmp;
135 VALUE_PAIR *array[1024]; /* way more than necessary */
138 for (vp = packet->vps; vp != NULL; vp = vp->next) {
142 if (size == 0) return;
144 for (i = 0; i < size - 1; i++) {
145 for (j = 0; j < size - 1 - i; j++) {
146 if (array[j + 1]->attribute < array[j]->attribute) {
148 array[j] = array[j + 1];
155 * And put them back again.
157 vp = packet->vps = array[0];
158 for (i = 1; i < size; i++) {
166 static void tv_sub(const struct timeval *end, const struct timeval *start,
167 struct timeval *elapsed)
169 elapsed->tv_sec = end->tv_sec - start->tv_sec;
170 if (elapsed->tv_sec > 0) {
172 elapsed->tv_usec = USEC;
174 elapsed->tv_usec = 0;
176 elapsed->tv_usec += end->tv_usec;
177 elapsed->tv_usec -= start->tv_usec;
179 if (elapsed->tv_usec >= USEC) {
180 elapsed->tv_usec -= USEC;
185 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
187 /* Just a counter of how many packets we've had */
188 static int count = 1;
189 /* Define pointers for packet's attributes */
190 const struct ethernet_header *ethernet; /* The ethernet header */
191 const struct ip_header *ip; /* The IP header */
192 const struct udp_header *udp; /* The UDP header */
193 const uint8_t *payload; /* Packet payload */
194 /* And define the size of the structures we're using */
195 int size_ethernet = sizeof(struct ethernet_header);
196 int size_ip = sizeof(struct ip_header);
197 int size_udp = sizeof(struct udp_header);
199 RADIUS_PACKET *packet;
200 struct timeval elapsed;
202 args = args; /* -Wunused */
204 /* Define our packet's attributes */
206 if ((data[0] == 2) && (data[1] == 0) &&
207 (data[2] == 0) && (data[3] == 0)) {
208 ip = (const struct ip_header*) (data + 4);
211 ethernet = (const struct ethernet_header*)(data);
212 ip = (const struct ip_header*)(data + size_ethernet);
214 udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
215 payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);
217 packet = malloc(sizeof(*packet));
219 fprintf(stderr, "Out of memory\n");
223 memset(packet, 0, sizeof(*packet));
224 packet->src_ipaddr.af = AF_INET;
225 packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
226 packet->src_port = ntohs(udp->udp_sport);
227 packet->dst_ipaddr.af = AF_INET;
228 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
229 packet->dst_port = ntohs(udp->udp_dport);
231 packet->data = payload;
232 packet->data_len = header->len - (payload - data);
234 if (!rad_packet_ok(packet, 0)) {
235 printf("Packet: %s\n", fr_strerror());
237 printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
238 printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
239 printf("\tType: %s\n", fr_packet_codes[packet->code]);
246 * Decode the data without bothering to check the signatures.
248 if (rad_decode(packet, NULL, radius_secret) != 0) {
254 if (filter_vps && filter_packet(packet)) {
256 DEBUG("Packet number %d doesn't match\n", count++);
261 pcap_dump((void *) pcap_dumper, header, data);
265 printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
267 /* Print the RADIUS packet */
268 printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
269 printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
270 if (fr_debug_flag) printf("\t(%d packets)", count++);
272 if (!start_pcap.tv_sec) {
273 start_pcap = header->ts;
276 tv_sub(&header->ts, &start_pcap, &elapsed);
278 printf("\t+%u.%03u", (unsigned int) elapsed.tv_sec,
279 (unsigned int) elapsed.tv_usec / 1000);
280 if (!minimal) printf("\n");
281 if (!minimal && packet->vps) {
282 if (do_sort) sort(packet);
284 vp_printlist(stdout, packet->vps);
285 pairfree(&packet->vps);
292 * If we're doing filtering, Access-Requests are cached
293 * in the filter tree.
296 ((packet->code != PW_AUTHENTICATION_REQUEST) &&
297 (packet->code != PW_ACCOUNTING_REQUEST))) {
302 static void NEVER_RETURNS usage(int status)
304 FILE *output = status ? stderr : stdout;
305 fprintf(output, "usage: radsniff [options]\n");
306 fprintf(output, "options:\n");
307 fprintf(output, "\t-c count\tNumber of packets to capture.\n");
308 fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
309 fprintf(output, "\t-F\t\tFilter PCAP file from stdin to stdout.\n");
310 fprintf(output, "\t\t\tOutput file will contain RADIUS packets.\n");
311 fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813)\n");
312 fprintf(output, "\t-h\t\tPrint this help message.\n");
313 fprintf(output, "\t-i interface\tInterface to capture.\n");
314 fprintf(output, "\t-I filename\tRead packets from filename.\n");
315 fprintf(output, "\t-m\t\tPrint packet headers only, not contents.\n");
316 fprintf(output, "\t-p port\t\tListen for packets on port.\n");
317 fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
318 fprintf(output, "\t-s secret\tRADIUS secret.\n");
319 fprintf(output, "\t-S\t\tSort attributes in the packet.\n");
320 fprintf(output, "\t\t\tUsed to compare server results.\n");
321 fprintf(output, "\t-x\t\tPrint out debugging information.\n");
325 int main(int argc, char *argv[])
327 char *dev; /* sniffing device */
328 char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
329 pcap_t *descr; /* sniff handler */
330 struct bpf_program fp; /* hold compiled program */
331 bpf_u_int32 maskp; /* subnet mask */
332 bpf_u_int32 netp; /* ip */
334 char *pcap_filter = NULL;
335 char *radius_filter = NULL;
336 char *filename = NULL;
337 char *dump_file = NULL;
338 int packet_count = -1; /* how many packets to sniff */
341 const char *radius_dir = RADIUS_DIR;
343 int filter_stdin = 0;
346 dev = pcap_lookupdev(errbuf);
349 while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:mp:r:s:Sw:xX")) != EOF) {
353 packet_count = atoi(optarg);
354 if (packet_count <= 0) {
355 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
366 pcap_filter = optarg;
384 radius_filter = optarg;
387 radius_secret = optarg;
396 case 'X': /* for backwards compatibility */
405 * Cross-check command-line arguments.
407 if (filter_stdin && (filename || dump_file)) usage(1);
409 #ifndef HAVE_PCAP_FOPEN_OFFLINE
411 fr_perror("-F is unsupported on this platform.");
417 pcap_filter = buffer;
418 snprintf(buffer, sizeof(buffer), "udp port %d or %d",
423 * There are many times where we don't need the dictionaries.
425 if (fr_debug_flag || radius_filter) {
426 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
427 fr_perror("radsniff");
433 parsecode = userparse(radius_filter, &filter_vps);
434 if (parsecode == T_OP_INVALID) {
435 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror());
439 fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
443 filter_tree = rbtree_create((rbcmp) fr_packet_cmp,
446 fprintf(stderr, "radsniff: Failed creating filter tree\n");
452 pcap_lookupnet(dev, &netp, &maskp, errbuf);
454 /* Print device to the user */
456 if (dev) printf("Device: [%s]\n", dev);
457 if (packet_count > 0) {
458 printf("Num of packets: [%d]\n",
461 printf("PCAP filter: [%s]\n", pcap_filter);
463 printf("RADIUS filter:\n");
464 vp_printlist(stdout, filter_vps);
466 printf("RADIUS secret: [%s]\n", radius_secret);
469 /* Open the device so we can spy */
471 descr = pcap_open_offline(filename, errbuf);
473 #ifdef HAVE_PCAP_FOPEN_OFFLINE
474 } else if (filter_stdin) {
475 descr = pcap_fopen_offline(stdin, errbuf);
479 fprintf(stderr, "radsniff: No filename or device was specified.\n");
483 descr = pcap_open_live(dev, 65536, 1, 0, errbuf);
487 fprintf(stderr, "radsniff: pcap_open_live failed (%s)\n", errbuf);
492 pcap_dumper = pcap_dump_open(descr, dump_file);
494 fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(descr));
498 #ifdef HAVE_PCAP_DUMP_FOPEN
499 } else if (filter_stdin) {
500 pcap_dumper = pcap_dump_fopen(descr, stdout);
502 fprintf(stderr, "radsniff: Failed opening stdout: %s\n", pcap_geterr(descr));
509 /* Apply the rules */
510 if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
512 fprintf(stderr, "radsniff: pcap_compile failed\n");
515 if (pcap_setfilter(descr, &fp) == -1)
517 fprintf(stderr, "radsniff: pcap_setfilter failed\n");
521 /* Now we can set our callback function */
522 pcap_loop(descr, packet_count, got_packet, NULL);
525 if (filter_tree) rbtree_free(filter_tree);
527 DEBUG("Done sniffing\n");