static const char *radius_secret = "testing123";
static VALUE_PAIR *filter_vps = NULL;
+#undef DEBUG
+#define DEBUG if (fr_debug_flag) printf
-static const char *packet_codes[] = {
- "",
- "Access-Request",
- "Access-Accept",
- "Access-Reject",
- "Accounting-Request",
- "Accounting-Response",
- "Accounting-Status",
- "Password-Request",
- "Password-Accept",
- "Password-Reject",
- "Accounting-Message",
- "Access-Challenge",
- "Status-Server",
- "Status-Client",
- "14",
- "15",
- "16",
- "17",
- "18",
- "19",
- "20",
- "Resource-Free-Request",
- "Resource-Free-Response",
- "Resource-Query-Request",
- "Resource-Query-Response",
- "Alternate-Resource-Reclaim-Request",
- "NAS-Reboot-Request",
- "NAS-Reboot-Response",
- "28",
- "Next-Passcode",
- "New-Pin",
- "Terminate-Session",
- "Password-Expired",
- "Event-Request",
- "Event-Response",
- "35",
- "36",
- "37",
- "38",
- "39",
- "Disconnect-Request",
- "Disconnect-ACK",
- "Disconnect-NAK",
- "CoF-Request",
- "CoF-ACK",
- "CoF-NAK",
- "46",
- "47",
- "48",
- "49",
- "IP-Address-Allocate",
- "IP-Address-Release"
-};
+static int minimal = 0;
+static int do_sort = 0;
+struct timeval start_pcap = {0, 0};
+static rbtree_t *filter_tree = NULL;
+static pcap_dumper_t *pcap_dumper = NULL;
-/*
- * Stolen from rad_recv() in ../lib/radius.c
- */
-static RADIUS_PACKET *init_packet(const uint8_t *data, size_t data_len)
-{
- RADIUS_PACKET *packet;
-
- /*
- * Allocate the new request data structure
- */
- if ((packet = malloc(sizeof(*packet))) == NULL) {
- librad_log("out of memory");
- return NULL;
- }
- memset(packet, 0, sizeof(*packet));
-
- packet->data = data;
- packet->data_len = data_len;
-
- if (!rad_packet_ok(packet)) {
- rad_free(&packet);
- return NULL;
- }
-
- /*
- * Explicitely set the VP list to empty.
- */
- packet->vps = NULL;
-
- return packet;
-}
+typedef int (*rbcmp)(const void *, const void *);
static int filter_packet(RADIUS_PACKET *packet)
{
fail++;
}
}
+
+
if (fail == 0 && pass != 0) {
- return 0;
+ /*
+ * Cache authentication requests, as the replies
+ * may not match the RADIUS filter.
+ */
+ if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
+ (packet->code == PW_ACCOUNTING_REQUEST)) {
+ rbtree_deletebydata(filter_tree, packet);
+
+ if (!rbtree_insert(filter_tree, packet)) {
+ oom:
+ fprintf(stderr, "radsniff: Out of memory\n");
+ exit(1);
+ }
+ }
+ return 0; /* matched */
}
+ /*
+ * Don't create erroneous matches.
+ */
+ if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
+ (packet->code == PW_ACCOUNTING_REQUEST)) {
+ rbtree_deletebydata(filter_tree, packet);
+ return 1;
+ }
+
+ /*
+ * Else see if a previous Access-Request
+ * matched. If so, also print out the
+ * matching accept, reject, or challenge.
+ */
+ if ((packet->code == PW_AUTHENTICATION_ACK) ||
+ (packet->code == PW_AUTHENTICATION_REJECT) ||
+ (packet->code == PW_ACCESS_CHALLENGE) ||
+ (packet->code == PW_ACCOUNTING_RESPONSE)) {
+ RADIUS_PACKET *reply;
+
+ /*
+ * This swaps the various fields.
+ */
+ reply = rad_alloc_reply(packet);
+ if (!reply) goto oom;
+
+ compare = 1;
+ if (rbtree_finddata(filter_tree, reply)) {
+ compare = 0;
+ }
+
+ rad_free(&reply);
+ return compare;
+ }
+
return 1;
}
-static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *packet)
+/*
+ * Bubble goodness
+ */
+static void sort(RADIUS_PACKET *packet)
+{
+ int i, j, size;
+ VALUE_PAIR *vp, *tmp;
+ VALUE_PAIR *array[1024]; /* way more than necessary */
+
+ size = 0;
+ for (vp = packet->vps; vp != NULL; vp = vp->next) {
+ array[size++] = vp;
+ }
+
+ if (size == 0) return;
+
+ for (i = 0; i < size - 1; i++) {
+ for (j = 0; j < size - 1 - i; j++) {
+ if (array[j + 1]->attribute < array[j]->attribute) {
+ tmp = array[j];
+ array[j] = array[j + 1];
+ array[j + 1] = tmp;
+ }
+ }
+ }
+
+ /*
+ * And put them back again.
+ */
+ vp = packet->vps = array[0];
+ for (i = 1; i < size; i++) {
+ vp->next = array[i];
+ vp = array[i];
+ }
+ vp->next = NULL;
+}
+
+#define USEC 1000000
+static void tv_sub(const struct timeval *end, const struct timeval *start,
+ struct timeval *elapsed)
+{
+ elapsed->tv_sec = end->tv_sec - start->tv_sec;
+ if (elapsed->tv_sec > 0) {
+ elapsed->tv_sec--;
+ elapsed->tv_usec = USEC;
+ } else {
+ elapsed->tv_usec = 0;
+ }
+ elapsed->tv_usec += end->tv_usec;
+ elapsed->tv_usec -= start->tv_usec;
+
+ if (elapsed->tv_usec >= USEC) {
+ elapsed->tv_usec -= USEC;
+ elapsed->tv_sec++;
+ }
+}
+
+static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
{
/* Just a counter of how many packets we've had */
static int count = 1;
int size_ip = sizeof(struct ip_header);
int size_udp = sizeof(struct udp_header);
/* For FreeRADIUS */
- RADIUS_PACKET *request;
+ RADIUS_PACKET *packet;
+ struct timeval elapsed;
args = args; /* -Wunused */
/* Define our packet's attributes */
- ethernet = (const struct ethernet_header*)(packet);
- ip = (const struct ip_header*)(packet + size_ethernet);
- udp = (const struct udp_header*)(packet + size_ethernet + size_ip);
- payload = (const uint8_t *)(packet + size_ethernet + size_ip + size_udp);
-
- /* Read the RADIUS packet structure */
- request = init_packet(payload, header->len - size_ethernet - size_ip - size_udp);
- if (request == NULL) {
- librad_perror("check");
+
+ if ((data[0] == 2) && (data[1] == 0) &&
+ (data[2] == 0) && (data[3] == 0)) {
+ ip = (const struct ip_header*) (data + 4);
+
+ } else {
+ ethernet = (const struct ethernet_header*)(data);
+ ip = (const struct ip_header*)(data + size_ethernet);
+ }
+ udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
+ payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);
+
+ packet = malloc(sizeof(*packet));
+ if (!packet) {
+ fprintf(stderr, "Out of memory\n");
+ return;
+ }
+
+ memset(packet, 0, sizeof(*packet));
+ packet->src_ipaddr.af = AF_INET;
+ packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
+ packet->src_port = ntohs(udp->udp_sport);
+ packet->dst_ipaddr.af = AF_INET;
+ packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
+ packet->dst_port = ntohs(udp->udp_dport);
+
+ packet->data = payload;
+ packet->data_len = header->len - (payload - data);
+
+ if (!rad_packet_ok(packet, 0)) {
+ printf("Packet: %s\n", fr_strerror());
+
+ printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
+ printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
+ printf("\tType: %s\n", fr_packet_codes[packet->code]);
+
+ free(packet);
return;
}
- request->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
- request->src_port = ntohs(udp->udp_sport);
- request->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
- request->dst_port = ntohs(udp->udp_dport);
/*
* Decode the data without bothering to check the signatures.
*/
- if (rad_decode(request, NULL, radius_secret) != 0) {
- librad_perror("decode");
+ if (rad_decode(packet, NULL, radius_secret) != 0) {
+ free(packet);
+ fr_perror("decode");
return;
}
- if (filter_vps && filter_packet(request)) {
- /* printf("Packet number %d doesn't match\n", count++); */
+
+ if (filter_vps && filter_packet(packet)) {
+ free(packet);
+ DEBUG("Packet number %d doesn't match\n", count++);
return;
}
+ if (pcap_dumper) {
+ pcap_dump((void *) pcap_dumper, header, data);
+ goto check_filter;
+ }
+
+ printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
+
/* Print the RADIUS packet */
- printf("Packet number %d has just been sniffed\n", count++);
- printf("\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
- printf("\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
- printf("\tType: %s\n", packet_codes[request->code]);
- if (request->vps != NULL) {
- vp_printlist(stdout, request->vps);
- pairfree(&request->vps);
+ printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
+ printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
+ if (fr_debug_flag) printf("\t(%d packets)", count++);
+
+ if (!start_pcap.tv_sec) {
+ start_pcap = header->ts;
+ }
+
+ tv_sub(&header->ts, &start_pcap, &elapsed);
+
+ printf("\t+%u.%03u", (unsigned int) elapsed.tv_sec,
+ (unsigned int) elapsed.tv_usec / 1000);
+ if (!minimal) printf("\n");
+ if (!minimal && packet->vps) {
+ if (do_sort) sort(packet);
+
+ vp_printlist(stdout, packet->vps);
+ pairfree(&packet->vps);
+ }
+ printf("\n");
+ fflush(stdout);
+
+ check_filter:
+ /*
+ * If we're doing filtering, Access-Requests are cached
+ * in the filter tree.
+ */
+ if (!filter_vps ||
+ ((packet->code != PW_AUTHENTICATION_REQUEST) &&
+ (packet->code != PW_ACCOUNTING_REQUEST))) {
+ free(packet);
}
- free(request);
}
static void NEVER_RETURNS usage(int status)
fprintf(output, "options:\n");
fprintf(output, "\t-c count\tNumber of packets to capture.\n");
fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
- fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
+ fprintf(output, "\t-F\t\tFilter PCAP file from stdin to stdout.\n");
+ fprintf(output, "\t\t\tOutput file will contain RADIUS packets.\n");
+ fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813)\n");
fprintf(output, "\t-h\t\tPrint this help message.\n");
fprintf(output, "\t-i interface\tInterface to capture.\n");
- fprintf(output, "\t-p port\tList for packets on port.\n");
+ fprintf(output, "\t-I filename\tRead packets from filename.\n");
+ fprintf(output, "\t-m\t\tPrint packet headers only, not contents.\n");
+ fprintf(output, "\t-p port\t\tListen for packets on port.\n");
fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
fprintf(output, "\t-s secret\tRADIUS secret.\n");
+ fprintf(output, "\t-S\t\tSort attributes in the packet.\n");
+ fprintf(output, "\t\t\tUsed to compare server results.\n");
+ fprintf(output, "\t-x\t\tPrint out debugging information.\n");
exit(status);
}
char buffer[1024];
char *pcap_filter = NULL;
char *radius_filter = NULL;
+ char *filename = NULL;
+ char *dump_file = NULL;
int packet_count = -1; /* how many packets to sniff */
int opt;
- LRAD_TOKEN parsecode;
+ FR_TOKEN parsecode;
const char *radius_dir = RADIUS_DIR;
int port = 1812;
+ int filter_stdin = 0;
/* Default device */
dev = pcap_lookupdev(errbuf);
/* Get options */
- while ((opt = getopt(argc, argv, "c:d:f:hi:p:r:s:")) != EOF) {
+ while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:mp:r:s:Sw:xX")) != EOF) {
switch (opt)
{
case 'c':
case 'd':
radius_dir = optarg;
break;
+ case 'F':
+ filter_stdin = 1;
+ break;
case 'f':
pcap_filter = optarg;
break;
case 'i':
dev = optarg;
break;
+ case 'I':
+ filename = optarg;
+ break;
+ case 'm':
+ minimal = 1;
+ break;
case 'p':
port = atoi(optarg);
break;
case 'r':
radius_filter = optarg;
- parsecode = userparse(radius_filter, &filter_vps);
- if (parsecode == T_OP_INVALID || filter_vps == NULL) {
- fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\"\n", optarg);
- exit(1);
- }
break;
case 's':
radius_secret = optarg;
break;
+ case 'S':
+ do_sort = 1;
+ break;
+ case 'w':
+ dump_file = optarg;
+ break;
+ case 'x':
+ case 'X': /* for backwards compatibility */
+ fr_debug_flag++;
+ break;
default:
usage(1);
}
}
+ /*
+ * Cross-check command-line arguments.
+ */
+ if (filter_stdin && (filename || dump_file)) usage(1);
+
+#ifndef HAVE_PCAP_FOPEN_OFFLINE
+ if (filter_stdin) {
+ fr_perror("-F is unsupported on this platform.");
+ return 1;
+ }
+#endif
+
if (!pcap_filter) {
pcap_filter = buffer;
- snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
- port, port + 1, port + 2);
+ snprintf(buffer, sizeof(buffer), "udp port %d or %d",
+ port, port + 1);
+ }
+
+ /*
+ * There are many times where we don't need the dictionaries.
+ */
+ if (fr_debug_flag || radius_filter) {
+ if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
+ fr_perror("radsniff");
+ return 1;
+ }
+ }
+
+ if (radius_filter) {
+ parsecode = userparse(radius_filter, &filter_vps);
+ if (parsecode == T_OP_INVALID) {
+ fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror());
+ exit(1);
+ }
+ if (!filter_vps) {
+ fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
+ exit(1);
+ }
+
+ filter_tree = rbtree_create((rbcmp) fr_packet_cmp,
+ free, 0);
+ if (!filter_tree) {
+ fprintf(stderr, "radsniff: Failed creating filter tree\n");
+ exit(1);
+ }
}
- if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
- librad_perror("radsniff");
- return 1;
- }
/* Set our device */
pcap_lookupnet(dev, &netp, &maskp, errbuf);
/* Print device to the user */
- printf("Device: [%s]\n", dev);
- if (packet_count > 0) {
- printf("Num of packets: [%d]\n", packet_count);
- }
- printf("PCAP filter: [%s]\n", pcap_filter);
- if (filter_vps != NULL) {
- printf("RADIUS filter:\n");
- vp_printlist(stdout, filter_vps);
+ if (fr_debug_flag) {
+ if (dev) printf("Device: [%s]\n", dev);
+ if (packet_count > 0) {
+ printf("Num of packets: [%d]\n",
+ packet_count);
+ }
+ printf("PCAP filter: [%s]\n", pcap_filter);
+ if (filter_vps) {
+ printf("RADIUS filter:\n");
+ vp_printlist(stdout, filter_vps);
+ }
+ printf("RADIUS secret: [%s]\n", radius_secret);
}
- printf("RADIUS secret: [%s]\n", radius_secret);
/* Open the device so we can spy */
- descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
+ if (filename) {
+ descr = pcap_open_offline(filename, errbuf);
+
+#ifdef HAVE_PCAP_FOPEN_OFFLINE
+ } else if (filter_stdin) {
+ descr = pcap_fopen_offline(stdin, errbuf);
+#endif
+
+ } else if (!dev) {
+ fprintf(stderr, "radsniff: No filename or device was specified.\n");
+ exit(1);
+
+ } else {
+ descr = pcap_open_live(dev, 65536, 1, 0, errbuf);
+ }
if (descr == NULL)
{
- printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
+ fprintf(stderr, "radsniff: pcap_open_live failed (%s)\n", errbuf);
exit(1);
}
+ if (dump_file) {
+ pcap_dumper = pcap_dump_open(descr, dump_file);
+ if (!pcap_dumper) {
+ fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(descr));
+ exit(1);
+ }
+
+#ifdef HAVE_PCAP_DUMP_FOPEN
+ } else if (filter_stdin) {
+ pcap_dumper = pcap_dump_fopen(descr, stdout);
+ if (!pcap_dumper) {
+ fprintf(stderr, "radsniff: Failed opening stdout: %s\n", pcap_geterr(descr));
+ exit(1);
+ }
+
+#endif
+ }
+
/* Apply the rules */
if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
{
- printf("radsniff: pcap_compile failed\n");
+ fprintf(stderr, "radsniff: pcap_compile failed\n");
exit(1);
}
if (pcap_setfilter(descr, &fp) == -1)
{
- printf("radsniff: pcap_setfilter failed\n");
+ fprintf(stderr, "radsniff: pcap_setfilter failed\n");
exit(1);
}
pcap_loop(descr, packet_count, got_packet, NULL);
pcap_close(descr);
- printf("Done sniffing\n");
+ if (filter_tree) rbtree_free(filter_tree);
+
+ DEBUG("Done sniffing\n");
return 0;
}