use new RCSID macro to prevent Id keyword from being optimized out
[freeradius.git] / src / main / radsniff.c
1 /*
2  *  radsniff.c  Display the RADIUS traffic on the network.
3  *
4  *  Version:    $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  *  Copyright 2006  The FreeRADIUS server project
21  *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/autoconf.h>
28
29 #include <pcap.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <unistd.h>
36
37 #define _LIBRADIUS 1
38 #include <freeradius-devel/radpaths.h>
39 #include <freeradius-devel/conf.h>
40 #include <freeradius-devel/libradius.h>
41 #include <freeradius-devel/radsniff.h>
42
43 static const char *radius_secret = "testing123";
44 static VALUE_PAIR *filter_vps = NULL;
45
46 static const char *packet_codes[] = {
47   "",
48   "Access-Request",
49   "Access-Accept",
50   "Access-Reject",
51   "Accounting-Request",
52   "Accounting-Response",
53   "Accounting-Status",
54   "Password-Request",
55   "Password-Accept",
56   "Password-Reject",
57   "Accounting-Message",
58   "Access-Challenge",
59   "Status-Server",
60   "Status-Client",
61   "14",
62   "15",
63   "16",
64   "17",
65   "18",
66   "19",
67   "20",
68   "Resource-Free-Request",
69   "Resource-Free-Response",
70   "Resource-Query-Request",
71   "Resource-Query-Response",
72   "Alternate-Resource-Reclaim-Request",
73   "NAS-Reboot-Request",
74   "NAS-Reboot-Response",
75   "28",
76   "Next-Passcode",
77   "New-Pin",
78   "Terminate-Session",
79   "Password-Expired",
80   "Event-Request",
81   "Event-Response",
82   "35",
83   "36",
84   "37",
85   "38",
86   "39",
87   "Disconnect-Request",
88   "Disconnect-ACK",
89   "Disconnect-NAK",
90   "CoF-Request",
91   "CoF-ACK",
92   "CoF-NAK",
93   "46",
94   "47",
95   "48",
96   "49",
97   "IP-Address-Allocate",
98   "IP-Address-Release"
99 };
100
101 /*
102  *      Stolen from rad_recv() in ../lib/radius.c
103  */
104 static RADIUS_PACKET *init_packet(const uint8_t *data, size_t data_len)
105 {
106         RADIUS_PACKET           *packet;
107
108         /*
109          *      Allocate the new request data structure
110          */
111         if ((packet = malloc(sizeof(*packet))) == NULL) {
112                 librad_log("out of memory");
113                 return NULL;
114         }
115         memset(packet, 0, sizeof(*packet));
116
117         packet->data = data;
118         packet->data_len = data_len;
119
120         if (!rad_packet_ok(packet)) {
121                 rad_free(&packet);
122                 return NULL;
123         }
124
125         /*
126          *      Explicitely set the VP list to empty.
127          */
128         packet->vps = NULL;
129
130         return packet;
131 }
132
133 static int filter_packet(RADIUS_PACKET *packet)
134 {
135         VALUE_PAIR *check_item;
136         VALUE_PAIR *vp;
137         unsigned int pass, fail;
138         int compare;
139
140         pass = fail = 0;
141         for (vp = packet->vps; vp != NULL; vp = vp->next) {
142                 for (check_item = filter_vps;
143                      check_item != NULL;
144                      check_item = check_item->next)
145                         if ((check_item->attribute == vp->attribute)
146                          && (check_item->operator != T_OP_SET)) {
147                                 compare = paircmp(check_item, vp);
148                                 if (compare == 1)
149                                         pass++;
150                                 else
151                                         fail++;
152                         }
153         }
154         if (fail == 0 && pass != 0) {
155                 return 0;
156         }
157
158         return 1;
159 }
160
161 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *packet)
162 {
163         /* Just a counter of how many packets we've had */
164         static int count = 1;
165         /* Define pointers for packet's attributes */
166         const struct ethernet_header *ethernet;  /* The ethernet header */
167         const struct ip_header *ip;              /* The IP header */
168         const struct udp_header *udp;            /* The UDP header */
169         const uint8_t *payload;                     /* Packet payload */
170         /* And define the size of the structures we're using */
171         int size_ethernet = sizeof(struct ethernet_header);
172         int size_ip = sizeof(struct ip_header);
173         int size_udp = sizeof(struct udp_header);
174         /* For FreeRADIUS */
175         RADIUS_PACKET *request;
176
177         args = args;            /* -Wunused */
178
179         /* Define our packet's attributes */
180         ethernet = (const struct ethernet_header*)(packet);
181         ip = (const struct ip_header*)(packet + size_ethernet);
182         udp = (const struct udp_header*)(packet + size_ethernet + size_ip);
183         payload = (const uint8_t *)(packet + size_ethernet + size_ip + size_udp);
184
185         /* Read the RADIUS packet structure */
186         request = init_packet(payload, header->len - size_ethernet - size_ip - size_udp);
187         if (request == NULL) {
188                 librad_perror("check");
189                 return;
190         }
191         request->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
192         request->src_port = ntohs(udp->udp_sport);
193         request->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
194         request->dst_port = ntohs(udp->udp_dport);
195
196         /*
197          *      Decode the data without bothering to check the signatures.
198          */
199         if (rad_decode(request, NULL, radius_secret) != 0) {
200                 librad_perror("decode");
201                 return;
202         }
203         if (filter_vps && filter_packet(request)) {
204                 /* printf("Packet number %d doesn't match\n", count++); */
205                 return;
206         }
207
208         /* Print the RADIUS packet */
209         printf("Packet number %d has just been sniffed\n", count++);
210         printf("\tFrom:    %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
211         printf("\tTo:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
212         printf("\tType:    %s\n", packet_codes[request->code]);
213         if (request->vps != NULL) {
214                 vp_printlist(stdout, request->vps);
215                 pairfree(&request->vps);
216         }
217         free(request);
218 }
219
220 static void NEVER_RETURNS usage(int status)
221 {
222         FILE *output = status ? stderr : stdout;
223         fprintf(output, "usage: radsniff [options]\n");
224         fprintf(output, "options:\n");
225         fprintf(output, "\t-c count\tNumber of packets to capture.\n");
226         fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
227         fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
228         fprintf(output, "\t-h\t\tPrint this help message.\n");
229         fprintf(output, "\t-i interface\tInterface to capture.\n");
230         fprintf(output, "\t-p port\tList for packets on port.\n");
231         fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
232         fprintf(output, "\t-s secret\tRADIUS secret.\n");
233         exit(status);
234 }
235
236 int main(int argc, char *argv[])
237 {
238         char *dev;                      /* sniffing device */
239         char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
240         pcap_t *descr;                  /* sniff handler */
241         struct bpf_program fp;          /* hold compiled program */
242         bpf_u_int32 maskp;              /* subnet mask */
243         bpf_u_int32 netp;               /* ip */
244         char buffer[1024];
245         char *pcap_filter = NULL;
246         char *radius_filter = NULL;
247         int packet_count = -1;          /* how many packets to sniff */
248         int opt;
249         LRAD_TOKEN parsecode;
250         const char *radius_dir = RADIUS_DIR;
251         int port = 1812;
252
253         /* Default device */
254         dev = pcap_lookupdev(errbuf);
255
256         /* Get options */
257         while ((opt = getopt(argc, argv, "c:d:f:hi:p:r:s:")) != EOF) {
258                 switch (opt)
259                 {
260                 case 'c':
261                         packet_count = atoi(optarg);
262                         if (packet_count <= 0) {
263                                 fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
264                                 exit(1);
265                         }
266                         break;
267                 case 'd':
268                         radius_dir = optarg;
269                         break;
270                 case 'f':
271                         pcap_filter = optarg;
272                         break;
273                 case 'h':
274                         usage(0);
275                         break;
276                 case 'i':
277                         dev = optarg;
278                         break;
279                 case 'p':
280                         port = atoi(optarg);
281                         break;
282                 case 'r':
283                         radius_filter = optarg;
284                         parsecode = userparse(radius_filter, &filter_vps);
285                         if (parsecode == T_OP_INVALID || filter_vps == NULL) {
286                                 fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\"\n", optarg);
287                                 exit(1);
288                         }
289                         break;
290                 case 's':
291                         radius_secret = optarg;
292                         break;
293                 default:
294                         usage(1);
295                 }
296         }
297
298         if (!pcap_filter) {
299                 pcap_filter = buffer;
300                 snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
301                          port, port + 1, port + 2);
302         }
303
304         if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
305                 librad_perror("radsniff");
306                 return 1;
307         }
308         /* Set our device */
309         pcap_lookupnet(dev, &netp, &maskp, errbuf);
310
311         /* Print device to the user */
312         printf("Device: [%s]\n", dev);
313         if (packet_count > 0) {
314                 printf("Num of packets: [%d]\n", packet_count);
315         }
316         printf("PCAP filter: [%s]\n", pcap_filter);
317         if (filter_vps != NULL) {
318                 printf("RADIUS filter:\n");
319                 vp_printlist(stdout, filter_vps);
320         }
321         printf("RADIUS secret: [%s]\n", radius_secret);
322
323         /* Open the device so we can spy */
324         descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
325         if (descr == NULL)
326         {
327                 printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
328                 exit(1);
329         }
330
331         /* Apply the rules */
332         if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
333         {
334                 printf("radsniff: pcap_compile failed\n");
335                 exit(1);
336         }
337         if (pcap_setfilter(descr, &fp) == -1)
338         {
339                 printf("radsniff: pcap_setfilter failed\n");
340                 exit(1);
341         }
342
343         /* Now we can set our callback function */
344         pcap_loop(descr, packet_count, got_packet, NULL);
345         pcap_close(descr);
346
347         printf("Done sniffing\n");
348         return 0;
349 }