From c48ea782095cdedd77bf5a74deb40817adc560e9 Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Fri, 9 Nov 2012 12:51:34 +0000 Subject: [PATCH] Add port 3799 to the default filter to capture CoA and Disconnect Support reading from stdin writing to * Support writing to stdout reading from * Add -v which prints out our version and the libpcap library version Remove -m and add -q to reduce debugging level Cleanup of code in main() --- src/main/radsniff.c | 427 +++++++++++++++++++++++++++----------------- 1 file changed, 260 insertions(+), 167 deletions(-) diff --git a/src/main/radsniff.c b/src/main/radsniff.c index 5da57c505af..cade9f6b0cc 100644 --- a/src/main/radsniff.c +++ b/src/main/radsniff.c @@ -35,19 +35,32 @@ RCSID("$Id$") static const char *radius_secret = "testing123"; static VALUE_PAIR *filter_vps = NULL; -#undef DEBUG -#define DEBUG if (fr_debug_flag) printf -static int minimal = 0; static int do_sort = 0; +static int to_stdout = 0; +static FILE *log_dst; + +#undef DEBUG1 +#define DEBUG1 if (fr_debug_flag > 2) fprintf +#undef DEBUG +#define DEBUG if (fr_debug_flag > 1) fprintf +#undef INFO +#define INFO if (fr_debug_flag > 0) fprintf + struct timeval start_pcap = {0, 0}; static rbtree_t *filter_tree = NULL; static rbtree_t *request_tree = NULL; -static pcap_dumper_t *pcap_dumper = NULL; +static pcap_dumper_t *out = NULL; static RADIUS_PACKET *nullpacket = NULL; typedef int (*rbcmp)(const void *, const void *); +static const char *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING +#ifdef RADIUSD_VERSION_COMMIT +" (git #" RADIUSD_VERSION_COMMIT ")" +#endif +; + static int filter_packet(RADIUS_PACKET *packet) { VALUE_PAIR *check_item; @@ -70,7 +83,6 @@ static int filter_packet(RADIUS_PACKET *packet) } } - if (fail == 0 && pass != 0) { /* * Cache authentication requests, as the replies @@ -184,26 +196,35 @@ static void tv_sub(const struct timeval *end, const struct timeval *start, } } -static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data) +static void got_packet(UNUSED 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; - /* Define pointers for packet's attributes */ - const struct ethernet_header *ethernet; /* The ethernet header */ - const struct ip_header *ip; /* The IP header */ - const struct udp_header *udp; /* The UDP header */ - const uint8_t *payload; /* Packet payload */ - /* And define the size of the structures we're using */ + + static int count = 1; /* Packets seen */ + + /* + * Define pointers for packet's attributes + */ + const struct ethernet_header *ethernet; /* The ethernet header */ + const struct ip_header *ip; /* The IP header */ + const struct udp_header *udp; /* The UDP header */ + const uint8_t *payload; /* Packet payload */ + + /* + * And define the size of the structures we're using + */ int size_ethernet = sizeof(struct ethernet_header); int size_ip = sizeof(struct ip_header); int size_udp = sizeof(struct udp_header); - /* For FreeRADIUS */ + + /* + * For FreeRADIUS + */ RADIUS_PACKET *packet, *original; struct timeval elapsed; - args = args; /* -Wunused */ - - /* Define our packet's attributes */ + /* + * Define our packet's attributes + */ if ((data[0] == 2) && (data[1] == 0) && (data[2] == 0) && (data[3] == 0)) { @@ -213,6 +234,7 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui 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); @@ -230,15 +252,15 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr; packet->dst_port = ntohs(udp->udp_dport); - packet->data = payload; + memcpy(&packet->data, &payload, sizeof(packet->data)); packet->data_len = header->len - (payload - data); if (!rad_packet_ok(packet, 0)) { - printf("Packet: %s\n", fr_strerror()); + DEBUG(log_dst, "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]); + DEBUG(log_dst, " From %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); + DEBUG(log_dst, " To: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); + DEBUG(log_dst, " Type: %s\n", fr_packet_codes[packet->code]); free(packet); return; @@ -267,7 +289,7 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui } /* - * Decode the data without bothering to check the signatures. + * Decode the data without bothering to check the signatures. */ if (rad_decode(packet, original, radius_secret) != 0) { free(packet); @@ -275,51 +297,64 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui return; } - /* we've seen a successfull reply to this, so delete it now */ + /* + * We've seen a successfull reply to this, so delete it now + */ if (original) rbtree_deletebydata(request_tree, original); if (filter_vps && filter_packet(packet)) { free(packet); - DEBUG("Packet number %d doesn't match\n", count++); + DEBUG(log_dst, "Packet number %d doesn't match\n", count++); return; } - if (pcap_dumper) { - pcap_dump((void *) pcap_dumper, header, data); + if (out) { + pcap_dump((void *) out, header, data); goto check_filter; } - printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id); - - /* Print the RADIUS packet */ - 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++); + INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id); + /* + * Print the RADIUS packet + */ + INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport)); + INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport)); + + DEBUG1(log_dst, "\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, + INFO(log_dst, "\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); + + if (fr_debug_flag > 1) { + DEBUG(log_dst, "\n"); + if (packet->vps) { + if (do_sort) sort(packet); + + vp_printlist(log_dst, packet->vps); + pairfree(&packet->vps); + } + } + + INFO(log_dst, "\n"); + + if (!to_stdout && (fr_debug_flag > 4)) { + rad_print_hex(packet); } - printf("\n"); - if (fr_debug_flag > 2) rad_print_hex(packet); - fflush(stdout); + + fflush(log_dst); check_filter: /* - * If we're doing filtering, Access-Requests are cached - * in the filter tree. + * If we're doing filtering, Access-Requests are cached in the + * filter tree. */ if (!filter_vps || ((packet->code != PW_AUTHENTICATION_REQUEST) && @@ -331,58 +366,66 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui static void NEVER_RETURNS usage(int status) { FILE *output = status ? stderr : stdout; - fprintf(output, "usage: radsniff [options]\n"); + fprintf(output, "Usage: radsniff [options]\n"); 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\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-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-w file\tWrite output packets to file.\n"); - fprintf(output, "\t-x\t\tPrint out debugging information.\n"); + fprintf(output, " -c Number of packets to capture.\n"); + fprintf(output, " -d Set dictionary directory.\n"); + fprintf(output, " -F Filter PCAP file from stdin to stdout.\n"); + fprintf(output, " -f PCAP filter (default is 'udp port or or 3799')\n"); + fprintf(output, " -h This help message.\n"); + fprintf(output, " -i Capture packets from interface (defaults to any if supported).\n"); + fprintf(output, " -I Read packets from file (overrides input of -F).\n"); + fprintf(output, " -p Filter packets by port (default is 1812).\n"); + fprintf(output, " -q Print less debugging information.\n"); + fprintf(output, " -r RADIUS attribute filter.\n"); + fprintf(output, " -s RADIUS secret.\n"); + fprintf(output, " -S Sort attributes in the packet (useful for diffing responses).\n"); + fprintf(output, " -v Show program version information.\n"); + fprintf(output, " -w Write output packets to file (overrides output of -F).\n"); + fprintf(output, " -x Print more debugging information (defaults to -xx).\n"); exit(status); } int main(int argc, char *argv[]) { - char *dev; /* sniffing device */ - char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */ - pcap_t *descr; /* sniff handler */ - struct bpf_program fp; /* hold compiled program */ - bpf_u_int32 maskp; /* subnet mask */ - bpf_u_int32 netp; /* ip */ - char buffer[1024]; - char *pcap_filter = NULL; + const char *from_dev = NULL; /* Capture from device */ + const char *from_file = NULL; /* Read from pcap file */ + int from_stdin = 0; /* Read from stdin */ + + pcap_t *in; /* PCAP input handle */ + + int limit = -1; /* How many packets to sniff */ + + char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ + + char *to_file = NULL; /* PCAP output file */ + + char *pcap_filter = NULL; /* PCAP filter string */ char *radius_filter = NULL; - char *filename = NULL; - int printable_output = 1; - char *dump_file = NULL; - int packet_count = -1; /* how many packets to sniff */ + int port = 1812; + + struct bpf_program fp; /* Holds compiled filter */ + bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN; /* Device Subnet mask */ + bpf_u_int32 ip_addr = 0; /* Device IP */ + + char buffer[1024]; + int opt; FR_TOKEN parsecode; const char *radius_dir = RADIUS_DIR; - int port = 1812; - int filter_stdin = 0; - - /* Default device */ - dev = pcap_lookupdev(errbuf); + + fr_debug_flag = 2; + log_dst = stdout; - /* Get options */ - while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:mp:r:s:Sw:xX")) != EOF) { + /* + * Get options + */ + while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) { switch (opt) { case 'c': - packet_count = atoi(optarg); - if (packet_count <= 0) { + limit = atoi(optarg); + if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg); exit(1); } @@ -391,8 +434,8 @@ int main(int argc, char *argv[]) radius_dir = optarg; break; case 'F': - filter_stdin = 1; - printable_output = 0; + from_stdin = 1; + to_stdout = 1; break; case 'f': pcap_filter = optarg; @@ -401,17 +444,19 @@ int main(int argc, char *argv[]) usage(0); break; case 'i': - dev = optarg; + from_dev = optarg; break; case 'I': - filename = optarg; - break; - case 'm': - minimal = 1; + from_file = optarg; break; case 'p': port = atoi(optarg); break; + case 'q': + if (fr_debug_flag > 0) { + fr_debug_flag--; + } + break; case 'r': radius_filter = optarg; break; @@ -421,156 +466,204 @@ int main(int argc, char *argv[]) case 'S': do_sort = 1; break; + case 'v': + INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version()); + exit(0); + break; case 'w': - dump_file = optarg; - printable_output = 0; + to_file = optarg; break; case 'x': - case 'X': /* for backwards compatibility */ + case 'X': fr_debug_flag++; break; default: - usage(1); + usage(64); } } - + + /* What's the point in specifying -F ?! */ + if (from_stdin && from_file && to_file) { + usage(64); + } + + /* Can't read from both... */ + if (from_file && from_dev) { + usage(64); + } + + /* Reading from file overrides stdin */ + if (from_stdin && (from_file || from_dev)) { + from_stdin = 0; + } + + /* Writing to file overrides stdout */ + if (to_file && to_stdout) { + to_stdout = 0; + } + /* - * Cross-check command-line arguments. + * If were writing pcap data stdout we *really* don't want to send + * logging there as well. */ - if (filter_stdin && (filename || dump_file)) usage(1); + log_dst = to_stdout ? stderr : stdout; -#ifndef HAVE_PCAP_FOPEN_OFFLINE - if (filter_stdin) { - fr_perror("-F is unsupported on this platform."); - return 1; +#if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) + if (from_stdin || to_stdout) { + fprintf(stderr, "radsniff: PCAP streams not supported.\n"); + exit(64); } #endif if (!pcap_filter) { pcap_filter = buffer; - snprintf(buffer, sizeof(buffer), "udp port %d or %d", - port, port + 1); + snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", + port, port + 1, 3799); } /* - * There are times when we don't need the dictionaries. + * There are times when we don't need the dictionaries. */ - if (printable_output) { + if (!to_stdout) { if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); - return 1; + exit(64); } } 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); + fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror()); + exit(64); } + if (!filter_vps) { fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter); - exit(1); + exit(64); } - filter_tree = rbtree_create((rbcmp) fr_packet_cmp, - free, 0); + filter_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!filter_tree) { fprintf(stderr, "radsniff: Failed creating filter tree\n"); exit(1); } } - /* setup the request tree */ + /* + * Setup the request tree + */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, free, 0); if (!request_tree) { fprintf(stderr, "radsniff: Failed creating request tree\n"); exit(1); } - /* allocate a null packet for decrypting attributes in CoA requests */ + /* + * Allocate a null packet for decrypting attributes in CoA requests + */ nullpacket = rad_alloc(0); if (!nullpacket) { fprintf(stderr, "radsniff: Out of memory\n"); exit(1); } - /* Set our device */ - pcap_lookupnet(dev, &netp, &maskp, errbuf); - - /* Print device to the user */ - if (fr_debug_flag) { - if (dev) printf("Device: [%s]\n", dev); - if (packet_count > 0) { - printf("Num of packets: [%d]\n", - packet_count); + /* + * Get the default capture device + */ + if (!from_stdin && !from_file && !from_dev) { + from_dev = pcap_lookupdev(errbuf); + if (!from_dev) { + fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf); + exit(1); } - printf("PCAP filter: [%s]\n", pcap_filter); - if (filter_vps) { - printf("RADIUS filter:\n"); - vp_printlist(stdout, filter_vps); + + INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev); + } + + /* + * Print captures values which will be used + */ + if (fr_debug_flag > 2) { + DEBUG1(log_dst, "Sniffing with options:\n"); + if (from_dev) DEBUG1(log_dst, " Device : [%s]\n", from_dev); + if (limit > 0) DEBUG1(log_dst, " Capture limit (packets) : [%d]\n", limit); + DEBUG1(log_dst, " PCAP filter : [%s]\n", pcap_filter); + DEBUG1(log_dst, " RADIUS secret : [%s]\n", radius_secret); + if (filter_vps){DEBUG1(log_dst, " RADIUS filter :\n"); + vp_printlist(log_dst, filter_vps); } - printf("RADIUS secret: [%s]\n", radius_secret); } - /* Open the device so we can spy */ - if (filename) { - descr = pcap_open_offline(filename, errbuf); - + /* + * Figure out whether were doing a reading from a file, doing a live + * capture or reading from stdin. + */ + if (from_file) { + in = pcap_open_offline(from_file, errbuf); #ifdef HAVE_PCAP_FOPEN_OFFLINE - } else if (filter_stdin) { - descr = pcap_fopen_offline(stdin, errbuf); + } else if (from_stdin) { + in = pcap_fopen_offline(stdin, errbuf); #endif - - } else if (!dev) { - fprintf(stderr, "radsniff: No filename or device was specified.\n"); - exit(1); - + } else if (from_dev) { + pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf); + in = pcap_open_live(from_dev, 65536, 1, 1, errbuf); } else { - descr = pcap_open_live(dev, 65536, 1, 1, errbuf); + fprintf(stderr, "radsniff: No capture devices available\n"); } - if (descr == NULL) - { - fprintf(stderr, "radsniff: pcap_open_live failed (%s)\n", errbuf); + + if (!in) { + fprintf(stderr, "radsniff: Failed opening input (%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)); + if (to_file) { + out = pcap_dump_open(in, to_file); + if (!out) { + fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in)); 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)); + } else if (to_stdout) { + out = pcap_dump_fopen(in, stdout); + if (!out) { + fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in)); exit(1); } - #endif } - /* Apply the rules */ - if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1) - { - fprintf(stderr, "radsniff: pcap_compile failed\n"); + /* + * Apply the rules + */ + if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) { + fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } - if (pcap_setfilter(descr, &fp) == -1) - { - fprintf(stderr, "radsniff: pcap_setfilter failed\n"); + + if (pcap_setfilter(in, &fp) < 0) { + fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in)); exit(1); } - /* Now we can set our callback function */ - pcap_loop(descr, packet_count, got_packet, NULL); - pcap_close(descr); - - if (filter_tree) rbtree_free(filter_tree); - - DEBUG("Done sniffing\n"); + /* + * Enter the main capture loop... + */ + pcap_loop(in, limit, got_packet, NULL); + + /* + * ...were done capturing. + */ + pcap_close(in); + if (out) { + pcap_dump_close(out); + } + + if (filter_tree) { + rbtree_free(filter_tree); + } + + INFO(log_dst, "Done sniffing\n"); + return 0; } -- 2.47.3