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 rbtree_t *filter_tree = NULL;
42 typedef int (*rbcmp)(const void *, const void *);
44 static int filter_packet(RADIUS_PACKET *packet)
46 VALUE_PAIR *check_item;
48 unsigned int pass, fail;
52 for (vp = packet->vps; vp != NULL; vp = vp->next) {
53 for (check_item = filter_vps;
55 check_item = check_item->next)
56 if ((check_item->attribute == vp->attribute)
57 && (check_item->operator != T_OP_SET)) {
58 compare = paircmp(check_item, vp);
67 if (fail == 0 && pass != 0) {
69 * Cache authentication requests, as the replies
70 * may not match the RADIUS filter.
72 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
73 (packet->code == PW_ACCOUNTING_REQUEST)) {
74 rbtree_deletebydata(filter_tree, packet);
76 if (!rbtree_insert(filter_tree, packet)) {
78 fprintf(stderr, "radsniff: Out of memory\n");
82 return 0; /* matched */
86 * Don't create erroneous matches.
88 if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
89 (packet->code == PW_ACCOUNTING_REQUEST)) {
90 rbtree_deletebydata(filter_tree, packet);
95 * Else see if a previous Access-Request
96 * matched. If so, also print out the
97 * matching accept, reject, or challenge.
99 if ((packet->code == PW_AUTHENTICATION_ACK) ||
100 (packet->code == PW_AUTHENTICATION_REJECT) ||
101 (packet->code == PW_ACCESS_CHALLENGE) ||
102 (packet->code == PW_ACCOUNTING_RESPONSE)) {
103 RADIUS_PACKET *reply;
106 * This swaps the various fields.
108 reply = rad_alloc_reply(packet);
109 if (!reply) goto oom;
112 if (rbtree_finddata(filter_tree, reply)) {
123 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
125 /* Just a counter of how many packets we've had */
126 static int count = 1;
127 /* Define pointers for packet's attributes */
128 const struct ethernet_header *ethernet; /* The ethernet header */
129 const struct ip_header *ip; /* The IP header */
130 const struct udp_header *udp; /* The UDP header */
131 const uint8_t *payload; /* Packet payload */
132 /* And define the size of the structures we're using */
133 int size_ethernet = sizeof(struct ethernet_header);
134 int size_ip = sizeof(struct ip_header);
135 int size_udp = sizeof(struct udp_header);
137 RADIUS_PACKET *packet;
139 args = args; /* -Wunused */
141 /* Define our packet's attributes */
142 ethernet = (const struct ethernet_header*)(data);
143 ip = (const struct ip_header*)(data + size_ethernet);
144 udp = (const struct udp_header*)(data + size_ethernet + size_ip);
145 payload = (const uint8_t *)(data + size_ethernet + size_ip + size_udp);
147 packet = malloc(sizeof(*packet));
149 fprintf(stderr, "Out of memory\n");
153 memset(packet, 0, sizeof(*packet));
154 packet->src_ipaddr.af = AF_INET;
155 packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
156 packet->src_port = ntohs(udp->udp_sport);
157 packet->dst_ipaddr.af = AF_INET;
158 packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
159 packet->dst_port = ntohs(udp->udp_dport);
161 packet->data = payload;
162 packet->data_len = header->len - size_ethernet - size_ip - size_udp;
164 if (!rad_packet_ok(packet, 0)) {
167 fprintf(stderr, "\tFrom: %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
168 fprintf(stderr, "\tTo: %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
169 fprintf(stderr, "\tType: %s\n", fr_packet_codes[packet->code]);
176 * Decode the data without bothering to check the signatures.
178 if (rad_decode(packet, NULL, radius_secret) != 0) {
184 if (filter_vps && filter_packet(packet)) {
186 DEBUG("Packet number %d doesn't match\n", count++);
189 printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
191 /* Print the RADIUS packet */
192 printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
193 printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
194 if (fr_debug_flag) printf("\t(%d packets)", count++);
195 printf("\t%08x", header->ts.tv_sec);
197 if (packet->vps != NULL) {
198 vp_printlist(stdout, packet->vps);
199 pairfree(&packet->vps);
205 * If we're doing filtering, Access-Requests are cached
206 * in the filter tree.
209 ((packet->code != PW_AUTHENTICATION_REQUEST) &&
210 (packet->code != PW_ACCOUNTING_REQUEST))) {
215 static void NEVER_RETURNS usage(int status)
217 FILE *output = status ? stderr : stdout;
218 fprintf(output, "usage: radsniff [options]\n");
219 fprintf(output, "options:\n");
220 fprintf(output, "\t-c count\tNumber of packets to capture.\n");
221 fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
222 fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
223 fprintf(output, "\t-h\t\tPrint this help message.\n");
224 fprintf(output, "\t-i interface\tInterface to capture.\n");
225 fprintf(output, "\t-I filename\tRead packets from filename.\n");
226 fprintf(output, "\t-p port\tList for packets on port.\n");
227 fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
228 fprintf(output, "\t-s secret\tRADIUS secret.\n");
229 fprintf(output, "\t-X\t\tPrint out debugging information.\n");
233 int main(int argc, char *argv[])
235 char *dev; /* sniffing device */
236 char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
237 pcap_t *descr; /* sniff handler */
238 struct bpf_program fp; /* hold compiled program */
239 bpf_u_int32 maskp; /* subnet mask */
240 bpf_u_int32 netp; /* ip */
242 char *pcap_filter = NULL;
243 char *radius_filter = NULL;
244 char *filename = NULL;
245 int packet_count = -1; /* how many packets to sniff */
248 const char *radius_dir = RADIUS_DIR;
252 dev = pcap_lookupdev(errbuf);
255 while ((opt = getopt(argc, argv, "c:d:f:hi:I:p:r:s:X")) != EOF) {
259 packet_count = atoi(optarg);
260 if (packet_count <= 0) {
261 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
269 pcap_filter = optarg;
284 radius_filter = optarg;
287 radius_secret = optarg;
298 pcap_filter = buffer;
299 snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
300 port, port + 1, port + 2);
303 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
304 fr_perror("radsniff");
309 parsecode = userparse(radius_filter, &filter_vps);
310 if (parsecode == T_OP_INVALID) {
311 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\": %s\n", radius_filter, fr_strerror());
315 fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
319 filter_tree = rbtree_create((rbcmp) fr_packet_cmp,
322 fprintf(stderr, "radsniff: Failed creating filter tree\n");
328 pcap_lookupnet(dev, &netp, &maskp, errbuf);
330 /* Print device to the user */
332 if (dev) printf("Device: [%s]\n", dev);
333 if (packet_count > 0) {
334 printf("Num of packets: [%d]\n",
337 printf("PCAP filter: [%s]\n", pcap_filter);
339 printf("RADIUS filter:\n");
340 vp_printlist(stdout, filter_vps);
342 printf("RADIUS secret: [%s]\n", radius_secret);
345 /* Open the device so we can spy */
347 descr = pcap_open_offline(filename, errbuf);
349 descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
353 printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
357 /* Apply the rules */
358 if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
360 printf("radsniff: pcap_compile failed\n");
363 if (pcap_setfilter(descr, &fp) == -1)
365 printf("radsniff: pcap_setfilter failed\n");
369 /* Now we can set our callback function */
370 pcap_loop(descr, packet_count, got_packet, NULL);
373 if (filter_tree) rbtree_free(filter_tree);
375 DEBUG("Done sniffing\n");