]> mj.ucw.cz Git - netgrind.git/blob - netgrind/netgrind.c
Add histograms and recovery after SIGINT.
[netgrind.git] / netgrind / netgrind.c
1 /*
2  *      Netgrind -- The Network Traffic Analyser
3  *
4  *      (c) 2003 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 -c <count>      Stop after processing <count> packets\n\
121 -d <dir>        Dump connections to a given directory\n\
122 -D <dir>        Dump connections with more details\n\
123 -f <filter>     Apply filter expression\n\
124 -h <file>       Write packet histogram to <file>\n\
125 -s              Dump connection summary\n\
126 -t              Calculate statistics only\n\
127 -w              TCP: Wait for ACK before processing packets\n\
128 ");
129   exit(1);
130 }
131
132 static int max_packets = -1;
133 static byte *filter = NULL;
134
135 int main(int argc, char **argv)
136 {
137   char errbuf[PCAP_ERRBUF_SIZE];
138   pcap_t *pcap;
139   int c, dlt;
140   struct bpf_program filter_prog;
141   byte *histogram = NULL;
142
143   tcp_default_appl = &appl_mux;
144   while ((c = getopt(argc, argv, "ac:d:D:f:h:stw")) >= 0)
145     switch (c)
146       {
147       case 'a':
148         tcp_arrival_times = 1;
149         break;
150       case 'c':
151         max_packets = atol(optarg);
152         break;
153       case 'd':
154         tcp_default_appl = &appl_save;
155         save_dir = optarg;
156         break;
157       case 'D':
158         tcp_default_appl = &appl_asave;
159         save_dir = optarg;
160         break;
161       case 'f':
162         filter = optarg;
163         break;
164       case 'h':
165         histogram = optarg;
166         break;
167       case 'w':
168         tcp_wait_for_ack = 1;
169         break;
170       case 's':
171         tcp_default_appl = &appl_summary;
172         break;
173       case 't':
174         tcp_default_appl = &appl_sink;
175         break;
176       default:
177         usage();
178       }
179   if (optind == argc)
180     usage();
181
182   tcp_init();
183   if (histogram)
184     {
185       histogram_init(histogram);
186       histogram_add_stat("PcapIn", &stat_pcap_in);
187       histogram_add_stat("PcapIncomp", &stat_pcap_incomplete);
188       histogram_add_stat("LinkIn", &stat_link_in);
189       histogram_add_stat("LinkDwarf", &stat_link_dwarf);
190       histogram_add_stat("LinkUnkn", &stat_link_unknown);
191       histogram_add_stat("LinkArp", &stat_link_arp);
192       histogram_add_stat("IPIn", &stat_ip_in);
193       histogram_add_stat("IPBad", &stat_ip_invalid);
194       histogram_add_stat("IPUnint", &stat_ip_uninteresting);
195       histogram_add_stat("IPFrag", &stat_ip_fragmented);
196       histogram_add_stat("IPBadSum", &stat_ip_badsum);
197       histogram_add_stat("TCPIn", &stat_tcp_in);
198       histogram_add_stat("TCPBad", &stat_tcp_invalid);
199       histogram_add_stat("TCPBadSum", &stat_tcp_badsum);
200       histogram_add_stat("TCPUnmatch", &stat_tcp_unmatched);
201       histogram_add_stat("TCPOnClosed", &stat_tcp_on_closed);
202       histogram_add_stat("TCPBadState", &stat_tcp_bad_state);
203       histogram_add_int("FlowsTotal", &cnt_tcp_flows);
204       histogram_add_int("FlowsClosed", &cnt_tcp_causes[CAUSE_CLOSE]);
205       histogram_add_int("FlowsReset", &cnt_tcp_causes[CAUSE_RESET]);
206       histogram_add_int("FlowsTimeout", &cnt_tcp_causes[CAUSE_TIMEOUT]);
207       histogram_add_int("FlowsDoomsday", &cnt_tcp_causes[CAUSE_DOOMSDAY]);
208       histogram_add_int("FlowsBad", &cnt_tcp_causes[CAUSE_CORRUPT]);
209     }
210   signal(SIGINT, sigint_handler);
211
212   while (optind < argc)
213     {
214       fprintf(stderr, "Processing %s...\n", argv[optind]);
215       if (!(pcap = pcap_open_offline(argv[optind], errbuf)))
216         die("Unable to open %s", errbuf);
217       dlt = pcap_datalink(pcap);
218       if (!link_setup_handler(dlt))
219         die("Don't know how to handle data link type %d", dlt);
220       if (filter)
221         {
222           if (pcap_compile(pcap, &filter_prog, filter, 1, 0) < 0)
223             die("Error compiling filter: %s", pcap_geterr(pcap));
224           pcap_setfilter(pcap, &filter_prog);
225           pcap_freecode(&filter_prog);
226         }
227       if (!setjmp(stop_jump))
228         {
229           if (pcap_loop(pcap, max_packets, got_pcap_packet, NULL) < 0)
230             {
231               fprintf(stderr, "Capture failed: %s\n", pcap_geterr(pcap));
232               break;
233             }
234         }
235       else
236         {
237           fprintf(stderr, "Interrupted\n");
238           break;
239         }
240       pcap_close(pcap);
241       optind++;
242     }
243   tcp_cleanup(last_timestamp);
244   histogram_cleanup();
245   printf("# Netgrind statistics:\n");
246   printf("# Pcap: %Ld(%Ld) in, %Ld(%Ld) incomplete\n",
247          stat_pcap_in.packets, stat_pcap_in.bytes,
248          stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes);
249   printf("# Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
250          stat_link_in.packets, stat_link_in.bytes,
251          stat_link_dwarf.packets, stat_link_dwarf.bytes,
252          stat_link_unknown.packets, stat_link_unknown.bytes,
253          stat_link_arp.packets, stat_link_arp.bytes);
254   printf("# IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented, %Ld(%Ld) bad checksum\n",
255          stat_ip_in.packets, stat_ip_in.bytes,
256          stat_ip_invalid.packets, stat_ip_invalid.bytes,
257          stat_ip_uninteresting.packets, stat_ip_uninteresting.bytes,
258          stat_ip_fragmented.packets, stat_ip_fragmented.bytes,
259          stat_ip_badsum.packets, stat_ip_badsum.bytes);
260   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",
261          stat_tcp_in.packets, stat_tcp_in.bytes,
262          stat_tcp_invalid.packets, stat_tcp_invalid.bytes,
263          stat_tcp_badsum.packets, stat_tcp_badsum.bytes,
264          stat_tcp_unmatched.packets, stat_tcp_unmatched.bytes,
265          stat_tcp_on_closed.packets, stat_tcp_on_closed.bytes,
266          stat_tcp_bad_state.packets, stat_tcp_bad_state.bytes);
267   printf("# Flows: %d total: %d closed, %d reset, %d timed out, %d overlap end, %d corrupted\n",
268          cnt_tcp_flows, cnt_tcp_causes[CAUSE_CLOSE], cnt_tcp_causes[CAUSE_RESET],
269          cnt_tcp_causes[CAUSE_TIMEOUT], cnt_tcp_causes[CAUSE_DOOMSDAY], cnt_tcp_causes[CAUSE_CORRUPT]);
270   return 0;
271 }