]> mj.ucw.cz Git - netgrind.git/blobdiff - netgrind/netgrind.c
Add histograms and recovery after SIGINT.
[netgrind.git] / netgrind / netgrind.c
index a54797a74ea4341b6925a07459752cbc147d84bc..da020748a0340ca46fb3aef1cd01fab29db68a3d 100644 (file)
@@ -7,10 +7,6 @@
  *     of the GNU General Public License.
  */
 
-/*
- *  FIXME: TCP stats
- */
-
 #include "lib/lib.h"
 #include "netgrind/pkt.h"
 #include "netgrind/netgrind.h"
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <netinet/in.h>
 
 #include <pcap.h>
 
@@ -33,10 +33,43 @@ void die(byte *msg, ...)
   exit(1);
 }
 
+static volatile int stop_signal;
+
+static void sigint_handler(int arg UNUSED)
+{
+  stop_signal = 1;
+}
+
+/*** MANUAL MULTIPLEXER ***/
+
+static void mux_open(struct flow *f, u64 when)
+{
+  u32 saddr = ntohl(f->saddr);
+  u32 daddr = ntohl(f->daddr);
+  uns sport = ntohs(f->sport);
+  uns dport = ntohs(f->dport);
+  struct appl_hooks *appl = &appl_sink;
+
+  if (dport == 80 || dport == 8080 || dport == 8081 || dport == 3128)
+    appl = &appl_http;
+  f->appl = appl;
+  appl->open(f, when);
+}
+
+struct appl_hooks appl_mux = {
+  .open = mux_open,
+  .input = sink_input,
+  .close = sink_close
+};
+
 /*** PCAP INTERFACE ***/
 
 static void (*link_handler)(struct pkt *);
-static struct pkt_stats stat_pcap_incomplete;
+static struct pkt_stats stat_pcap_incomplete, stat_pcap_in;
+
+static uns in_count, start_sec;
+static u64 last_timestamp;
+static jmp_buf stop_jump;
 
 static int link_setup_handler(int dlt)
 {
@@ -49,60 +82,190 @@ static int link_setup_handler(int dlt)
 
 static void got_pcap_packet(u_char *userdata UNUSED, const struct pcap_pkthdr *hdr, const u_char *pkt)
 {
+  if (stop_signal)
+    longjmp(stop_jump, 1);
+  histogram_step((uns) hdr->ts.tv_sec);
+  stat_pcap_in.packets++;
+  stat_pcap_in.bytes += hdr->len;
   if (hdr->caplen != hdr->len)
     {
       stat_pcap_incomplete.packets++;
       stat_pcap_incomplete.bytes += hdr->len - hdr->caplen;
       return;
     }
+  if (!(in_count % 1024))
+    {
+      if (!in_count)
+       start_sec = hdr->ts.tv_sec;
+      fprintf(stderr, "%d packets, %d seconds, %d conns (%d open)\r",
+             in_count, (int)hdr->ts.tv_sec - start_sec,
+             cnt_tcp_flows, tcp_num_flows);
+      fflush(stderr);
+    }
+  in_count++;
   struct pkt *p = pkt_new(0, hdr->len);
   memcpy(pkt_append(p, hdr->len), pkt, hdr->len);
   p->timestamp = (u64)hdr->ts.tv_sec * 1000000 + hdr->ts.tv_usec;
+  last_timestamp = p->timestamp;
   link_handler(p);
 }
 
+/*** MAIN ***/
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: netgrind [<switches>] <capture-files>\n\
+\n\
+-a             TCP: Record arrival times instead of processing times\n\
+-c <count>     Stop after processing <count> packets\n\
+-d <dir>       Dump connections to a given directory\n\
+-D <dir>       Dump connections with more details\n\
+-f <filter>    Apply filter expression\n\
+-h <file>      Write packet histogram to <file>\n\
+-s             Dump connection summary\n\
+-t             Calculate statistics only\n\
+-w             TCP: Wait for ACK before processing packets\n\
+");
+  exit(1);
+}
+
+static int max_packets = -1;
+static byte *filter = NULL;
+
 int main(int argc, char **argv)
 {
   char errbuf[PCAP_ERRBUF_SIZE];
   pcap_t *pcap;
-  int dlt;
+  int c, dlt;
+  struct bpf_program filter_prog;
+  byte *histogram = NULL;
 
-  if (argc != 2)
-    die("Usage: netgrind <capture-file>");
+  tcp_default_appl = &appl_mux;
+  while ((c = getopt(argc, argv, "ac:d:D:f:h:stw")) >= 0)
+    switch (c)
+      {
+      case 'a':
+       tcp_arrival_times = 1;
+       break;
+      case 'c':
+       max_packets = atol(optarg);
+       break;
+      case 'd':
+       tcp_default_appl = &appl_save;
+       save_dir = optarg;
+       break;
+      case 'D':
+       tcp_default_appl = &appl_asave;
+       save_dir = optarg;
+       break;
+      case 'f':
+       filter = optarg;
+       break;
+      case 'h':
+       histogram = optarg;
+       break;
+      case 'w':
+       tcp_wait_for_ack = 1;
+       break;
+      case 's':
+       tcp_default_appl = &appl_summary;
+       break;
+      case 't':
+       tcp_default_appl = &appl_sink;
+       break;
+      default:
+       usage();
+      }
+  if (optind == argc)
+    usage();
 
   tcp_init();
+  if (histogram)
+    {
+      histogram_init(histogram);
+      histogram_add_stat("PcapIn", &stat_pcap_in);
+      histogram_add_stat("PcapIncomp", &stat_pcap_incomplete);
+      histogram_add_stat("LinkIn", &stat_link_in);
+      histogram_add_stat("LinkDwarf", &stat_link_dwarf);
+      histogram_add_stat("LinkUnkn", &stat_link_unknown);
+      histogram_add_stat("LinkArp", &stat_link_arp);
+      histogram_add_stat("IPIn", &stat_ip_in);
+      histogram_add_stat("IPBad", &stat_ip_invalid);
+      histogram_add_stat("IPUnint", &stat_ip_uninteresting);
+      histogram_add_stat("IPFrag", &stat_ip_fragmented);
+      histogram_add_stat("IPBadSum", &stat_ip_badsum);
+      histogram_add_stat("TCPIn", &stat_tcp_in);
+      histogram_add_stat("TCPBad", &stat_tcp_invalid);
+      histogram_add_stat("TCPBadSum", &stat_tcp_badsum);
+      histogram_add_stat("TCPUnmatch", &stat_tcp_unmatched);
+      histogram_add_stat("TCPOnClosed", &stat_tcp_on_closed);
+      histogram_add_stat("TCPBadState", &stat_tcp_bad_state);
+      histogram_add_int("FlowsTotal", &cnt_tcp_flows);
+      histogram_add_int("FlowsClosed", &cnt_tcp_causes[CAUSE_CLOSE]);
+      histogram_add_int("FlowsReset", &cnt_tcp_causes[CAUSE_RESET]);
+      histogram_add_int("FlowsTimeout", &cnt_tcp_causes[CAUSE_TIMEOUT]);
+      histogram_add_int("FlowsDoomsday", &cnt_tcp_causes[CAUSE_DOOMSDAY]);
+      histogram_add_int("FlowsBad", &cnt_tcp_causes[CAUSE_CORRUPT]);
+    }
+  signal(SIGINT, sigint_handler);
 
-  if (!(pcap = pcap_open_offline(argv[1], errbuf)))
-    die("Unable to open %s: %s", argv[1], errbuf);
-  dlt = pcap_datalink(pcap);
-  if (!link_setup_handler(dlt))
-    die("Don't know how to handle data link type %d", dlt);
-  if (pcap_loop(pcap, -1, got_pcap_packet, NULL) < 0)
-    die("Capture failed: %s", pcap_geterr(pcap));
-  tcp_cleanup();
-#if 0
-  struct pcap_stat stats;
-  if (pcap_stats(pcap, &stats))
-    die("pcap_stats: %s", pcap_geterr(pcap));
-  printf("libpcap stats: %d packets received, %d dropped\n", stats.ps_recv, stats.ps_drop);
-#endif
-  printf("Pcap: %Ld(%Ld) incomplete\n",
+  while (optind < argc)
+    {
+      fprintf(stderr, "Processing %s...\n", argv[optind]);
+      if (!(pcap = pcap_open_offline(argv[optind], errbuf)))
+       die("Unable to open %s", errbuf);
+      dlt = pcap_datalink(pcap);
+      if (!link_setup_handler(dlt))
+       die("Don't know how to handle data link type %d", dlt);
+      if (filter)
+       {
+         if (pcap_compile(pcap, &filter_prog, filter, 1, 0) < 0)
+           die("Error compiling filter: %s", pcap_geterr(pcap));
+         pcap_setfilter(pcap, &filter_prog);
+         pcap_freecode(&filter_prog);
+       }
+      if (!setjmp(stop_jump))
+       {
+         if (pcap_loop(pcap, max_packets, got_pcap_packet, NULL) < 0)
+           {
+             fprintf(stderr, "Capture failed: %s\n", pcap_geterr(pcap));
+             break;
+           }
+       }
+      else
+       {
+         fprintf(stderr, "Interrupted\n");
+         break;
+       }
+      pcap_close(pcap);
+      optind++;
+    }
+  tcp_cleanup(last_timestamp);
+  histogram_cleanup();
+  printf("# Netgrind statistics:\n");
+  printf("# Pcap: %Ld(%Ld) in, %Ld(%Ld) incomplete\n",
+        stat_pcap_in.packets, stat_pcap_in.bytes,
         stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes);
-  printf("Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
+  printf("Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
         stat_link_in.packets, stat_link_in.bytes,
         stat_link_dwarf.packets, stat_link_dwarf.bytes,
         stat_link_unknown.packets, stat_link_unknown.bytes,
         stat_link_arp.packets, stat_link_arp.bytes);
-  printf("IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented, %Ld(%Ld) bad checksum\n",
+  printf("IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented, %Ld(%Ld) bad checksum\n",
         stat_ip_in.packets, stat_ip_in.bytes,
         stat_ip_invalid.packets, stat_ip_invalid.bytes,
         stat_ip_uninteresting.packets, stat_ip_uninteresting.bytes,
         stat_ip_fragmented.packets, stat_ip_fragmented.bytes,
         stat_ip_badsum.packets, stat_ip_badsum.bytes);
-  printf("TCP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) bad checksum\n",
+  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",
         stat_tcp_in.packets, stat_tcp_in.bytes,
         stat_tcp_invalid.packets, stat_tcp_invalid.bytes,
-        stat_tcp_badsum.packets, stat_tcp_badsum.bytes);
-  pcap_close(pcap);
+        stat_tcp_badsum.packets, stat_tcp_badsum.bytes,
+        stat_tcp_unmatched.packets, stat_tcp_unmatched.bytes,
+        stat_tcp_on_closed.packets, stat_tcp_on_closed.bytes,
+        stat_tcp_bad_state.packets, stat_tcp_bad_state.bytes);
+  printf("# Flows: %d total: %d closed, %d reset, %d timed out, %d overlap end, %d corrupted\n",
+        cnt_tcp_flows, cnt_tcp_causes[CAUSE_CLOSE], cnt_tcp_causes[CAUSE_RESET],
+        cnt_tcp_causes[CAUSE_TIMEOUT], cnt_tcp_causes[CAUSE_DOOMSDAY], cnt_tcp_causes[CAUSE_CORRUPT]);
   return 0;
 }