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