]> mj.ucw.cz Git - netgrind.git/blob - netgrind/netgrind.c
TODO: A note on IPv6
[netgrind.git] / netgrind / netgrind.c
1 /*
2  *      Netgrind -- The Network Traffic Analyser
3  *
4  *      (c) 2003--2013 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "netgrind/pkt.h"
12 #include "netgrind/netgrind.h"
13
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <signal.h>
20 #include <setjmp.h>
21 #include <netinet/in.h>
22
23 #include <pcap.h>
24
25 void die(byte *msg, ...)
26 {
27   va_list args;
28
29   va_start(args, msg);
30   fputs("netgrind: ", stderr);
31   vfprintf(stderr, msg, args);
32   fputs("\n", stderr);
33   exit(1);
34 }
35
36 static volatile int stop_signal;
37
38 static void sigint_handler(int arg UNUSED)
39 {
40   stop_signal = 1;
41 }
42
43 /*** MANUAL MULTIPLEXER ***/
44
45 static void mux_open(struct flow *f, u64 when)
46 {
47   u32 saddr = ntohl(f->saddr);
48   u32 daddr = ntohl(f->daddr);
49   uns sport = ntohs(f->sport);
50   uns dport = ntohs(f->dport);
51   struct appl_hooks *appl = &appl_sink;
52
53   if (dport == 80 || dport == 8080 || dport == 8081 || dport == 3128)
54     appl = &appl_http;
55   f->appl = appl;
56   appl->open(f, when);
57 }
58
59 struct appl_hooks appl_mux = {
60   .open = mux_open,
61   .input = sink_input,
62   .close = sink_close
63 };
64
65 /*** PCAP INTERFACE ***/
66
67 static void (*link_handler)(struct pkt *);
68 static struct pkt_stats stat_pcap_incomplete, stat_pcap_in;
69
70 static uns in_count, start_sec;
71 static u64 last_timestamp;
72 static jmp_buf stop_jump;
73
74 static int link_setup_handler(int dlt)
75 {
76   switch (dlt)
77     {
78     case DLT_EN10MB:    link_handler = link_eth_got_packet; return 1;
79     default:            return 0;
80     }
81 }
82
83 static void got_pcap_packet(u_char *userdata UNUSED, const struct pcap_pkthdr *hdr, const u_char *pkt)
84 {
85   if (stop_signal)
86     longjmp(stop_jump, 1);
87   histogram_step((uns) hdr->ts.tv_sec);
88   stat_pcap_in.packets++;
89   stat_pcap_in.bytes += hdr->len;
90   if (hdr->caplen != hdr->len)
91     {
92       stat_pcap_incomplete.packets++;
93       stat_pcap_incomplete.bytes += hdr->len - hdr->caplen;
94       return;
95     }
96   if (!(in_count % 1024))
97     {
98       if (!in_count)
99         start_sec = hdr->ts.tv_sec;
100       fprintf(stderr, "%d packets, %d seconds, %d conns (%d open)\r",
101               in_count, (int)hdr->ts.tv_sec - start_sec,
102               cnt_tcp_flows, tcp_num_flows);
103       fflush(stderr);
104     }
105   in_count++;
106   struct pkt *p = pkt_new(0, hdr->len);
107   memcpy(pkt_append(p, hdr->len), pkt, hdr->len);
108   p->timestamp = (u64)hdr->ts.tv_sec * 1000000 + hdr->ts.tv_usec;
109   last_timestamp = p->timestamp;
110   link_handler(p);
111 }
112
113 /*** MAIN ***/
114
115 static void usage(void)
116 {
117   fprintf(stderr, "Usage: netgrind [<switches>] <capture-files>\n\
118 \n\
119 -a              TCP: Record arrival times instead of processing times\n\
120 -b              Accept broken checksums\n\
121 -c <count>      Stop after processing <count> packets\n\
122 -d <dir>        Dump connections to a given directory\n\
123 -D <dir>        Dump connections with more details\n\
124 -f <filter>     Apply filter expression\n\
125 -h <file>       Write packet histogram to <file>\n\
126 -s              Dump connection summary\n\
127 -t              Calculate statistics only\n\
128 -w              TCP: Wait for ACK before processing packets\n\
129 -x <dir>        Dump HTTP transactions to a given directory\n\
130 ");
131   exit(1);
132 }
133
134 static int max_packets = -1;
135 static byte *filter = NULL;
136
137 int main(int argc, char **argv)
138 {
139   char errbuf[PCAP_ERRBUF_SIZE];
140   pcap_t *pcap;
141   int c, dlt;
142   struct bpf_program filter_prog;
143   byte *histogram = NULL;
144
145   tcp_default_appl = &appl_mux;
146   while ((c = getopt(argc, argv, "abc:d:D:f:h:stwx:")) >= 0)
147     switch (c)
148       {
149       case 'a':
150         tcp_arrival_times = 1;
151         break;
152       case 'b':
153         accept_broken_checksums = 1;
154         break;
155       case 'c':
156         max_packets = atol(optarg);
157         break;
158       case 'd':
159         tcp_default_appl = &appl_save;
160         save_dir = optarg;
161         break;
162       case 'D':
163         tcp_default_appl = &appl_asave;
164         save_dir = optarg;
165         break;
166       case 'f':
167         filter = optarg;
168         break;
169       case 'h':
170         histogram = optarg;
171         break;
172       case 'w':
173         tcp_wait_for_ack = 1;
174         break;
175       case 's':
176         tcp_default_appl = &appl_summary;
177         break;
178       case 't':
179         tcp_default_appl = &appl_sink;
180         break;
181       case 'x':
182         http_log_dir = optarg;
183         break;
184       default:
185         usage();
186       }
187   if (optind == argc)
188     usage();
189
190   tcp_init();
191   if (histogram)
192     {
193       histogram_init(histogram);
194       histogram_add_stat("PcapIn", &stat_pcap_in);
195       histogram_add_stat("PcapIncomp", &stat_pcap_incomplete);
196       histogram_add_stat("LinkIn", &stat_link_in);
197       histogram_add_stat("LinkDwarf", &stat_link_dwarf);
198       histogram_add_stat("LinkUnkn", &stat_link_unknown);
199       histogram_add_stat("LinkArp", &stat_link_arp);
200       histogram_add_stat("IPIn", &stat_ip_in);
201       histogram_add_stat("IPBad", &stat_ip_invalid);
202       histogram_add_stat("IPUnint", &stat_ip_uninteresting);
203       histogram_add_stat("IPFrag", &stat_ip_fragmented);
204       histogram_add_stat("IPBadSum", &stat_ip_badsum);
205       histogram_add_stat("TCPIn", &stat_tcp_in);
206       histogram_add_stat("TCPBad", &stat_tcp_invalid);
207       histogram_add_stat("TCPBadSum", &stat_tcp_badsum);
208       histogram_add_stat("TCPUnmatch", &stat_tcp_unmatched);
209       histogram_add_stat("TCPOnClosed", &stat_tcp_on_closed);
210       histogram_add_stat("TCPBadState", &stat_tcp_bad_state);
211       histogram_add_int("FlowsTotal", &cnt_tcp_flows);
212       histogram_add_int("FlowsClosed", &cnt_tcp_causes[CAUSE_CLOSE]);
213       histogram_add_int("FlowsReset", &cnt_tcp_causes[CAUSE_RESET]);
214       histogram_add_int("FlowsTimeout", &cnt_tcp_causes[CAUSE_TIMEOUT]);
215       histogram_add_int("FlowsDoomsday", &cnt_tcp_causes[CAUSE_DOOMSDAY]);
216       histogram_add_int("FlowsBad", &cnt_tcp_causes[CAUSE_CORRUPT]);
217     }
218   signal(SIGINT, sigint_handler);
219
220   while (optind < argc)
221     {
222       fprintf(stderr, "Processing %s...\n", argv[optind]);
223       if (!(pcap = pcap_open_offline(argv[optind], errbuf)))
224         die("Unable to open %s", errbuf);
225       dlt = pcap_datalink(pcap);
226       if (!link_setup_handler(dlt))
227         die("Don't know how to handle data link type %d", dlt);
228       if (filter)
229         {
230           if (pcap_compile(pcap, &filter_prog, filter, 1, 0) < 0)
231             die("Error compiling filter: %s", pcap_geterr(pcap));
232           pcap_setfilter(pcap, &filter_prog);
233           pcap_freecode(&filter_prog);
234         }
235       if (!setjmp(stop_jump))
236         {
237           if (pcap_loop(pcap, max_packets, got_pcap_packet, NULL) < 0)
238             {
239               fprintf(stderr, "Capture failed: %s\n", pcap_geterr(pcap));
240               break;
241             }
242         }
243       else
244         {
245           fprintf(stderr, "Interrupted\n");
246           break;
247         }
248       pcap_close(pcap);
249       optind++;
250     }
251   tcp_cleanup(last_timestamp);
252   histogram_cleanup();
253   printf("\n");
254   printf("# Netgrind statistics:\n");
255   printf("# Pcap: %Ld(%Ld) in, %Ld(%Ld) incomplete\n",
256          stat_pcap_in.packets, stat_pcap_in.bytes,
257          stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes);
258   printf("# Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
259          stat_link_in.packets, stat_link_in.bytes,
260          stat_link_dwarf.packets, stat_link_dwarf.bytes,
261          stat_link_unknown.packets, stat_link_unknown.bytes,
262          stat_link_arp.packets, stat_link_arp.bytes);
263   printf("# IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented, %Ld(%Ld) bad checksum\n",
264          stat_ip_in.packets, stat_ip_in.bytes,
265          stat_ip_invalid.packets, stat_ip_invalid.bytes,
266          stat_ip_uninteresting.packets, stat_ip_uninteresting.bytes,
267          stat_ip_fragmented.packets, stat_ip_fragmented.bytes,
268          stat_ip_badsum.packets, stat_ip_badsum.bytes);
269   printf("# TCP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) bad checksum, %Ld(%Ld) unmatched, %Ld(%Ld) on closed connections, %Ld(%Ld) in unexpected state\n",
270          stat_tcp_in.packets, stat_tcp_in.bytes,
271          stat_tcp_invalid.packets, stat_tcp_invalid.bytes,
272          stat_tcp_badsum.packets, stat_tcp_badsum.bytes,
273          stat_tcp_unmatched.packets, stat_tcp_unmatched.bytes,
274          stat_tcp_on_closed.packets, stat_tcp_on_closed.bytes,
275          stat_tcp_bad_state.packets, stat_tcp_bad_state.bytes);
276   printf("# Flows: %d total: %d closed, %d reset, %d timed out, %d overlap end, %d corrupted\n",
277          cnt_tcp_flows, cnt_tcp_causes[CAUSE_CLOSE], cnt_tcp_causes[CAUSE_RESET],
278          cnt_tcp_causes[CAUSE_TIMEOUT], cnt_tcp_causes[CAUSE_DOOMSDAY], cnt_tcp_causes[CAUSE_CORRUPT]);
279   return 0;
280 }