]> mj.ucw.cz Git - netgrind.git/blobdiff - netgrind/netgrind.c
Working version of TCP analyser.
[netgrind.git] / netgrind / netgrind.c
index a25f02a3a75398f16802f0d0b7adbb943103ee1e..ca657465c162d2a1a0a56820df933cf03bbb922b 100644 (file)
@@ -8,17 +8,14 @@
  */
 
 #include "lib/lib.h"
-#include "netgrind/netgrind.h"
 #include "netgrind/pkt.h"
+#include "netgrind/netgrind.h"
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
+#include <getopt.h>
 
 #include <pcap.h>
 
@@ -33,158 +30,6 @@ void die(byte *msg, ...)
   exit(1);
 }
 
-/*** CHECKSUMMING ***/
-
-static uns tcpip_calc_checksum(void *data, uns len, uns csum)
-{
-  byte *x = data;
-
-  while (len >= 2)
-    {
-      csum += (x[0] << 8) | x[1];
-      if (csum & 0xffff0000)
-       {
-         csum &= 0x0000ffff;
-         csum++;
-       }
-      x += 2;
-      len -= 2;
-    }
-  if (len)
-    {
-      csum += x[0];
-      if (csum & 0xffff0000)
-       {
-         csum &= 0x0000ffff;
-         csum++;
-       }
-    }
-  return csum;
-}
-
-static inline uns tcpip_verify_checksum(uns csum)
-{
-  return (csum == 0xffff);
-}
-
-/*** TCP LAYER ***/
-
-static struct pkt_stats stat_tcp_in, stat_tcp_invalid;
-
-static void tcp_got_packet(struct iphdr *iph, struct pkt *p)
-{
-  struct tcphdr *tcph;
-  struct {
-    u32 src;
-    u32 dst;
-    byte zero;
-    byte proto;
-    u16 len;
-  } fakehdr;
-
-  pkt_account(&stat_tcp_in, p);
-  if (!(tcph = pkt_peek(p, sizeof(*tcph))))
-    goto invalid;
-  uns hdrlen = 4*tcph->doff;
-  if (hdrlen < sizeof(*tcph) || hdrlen > pkt_len(p))
-    goto invalid;
-  fakehdr.src = iph->saddr;
-  fakehdr.dst = iph->daddr;
-  fakehdr.zero = 0;
-  fakehdr.proto = IPPROTO_TCP;
-  fakehdr.len = htons(pkt_len(p));
-  uns sum = tcpip_calc_checksum(&fakehdr, sizeof(fakehdr), 0);
-  sum = tcpip_calc_checksum(p->data, pkt_len(p), sum);
-  if (!tcpip_verify_checksum(sum))
-    goto invalid;
-
-  /* Here we should do something with the packet */
-  pkt_free(p);
-  return;
-
- invalid:
-  pkt_account(&stat_tcp_invalid, p);
-  pkt_free(p);
-}
-
-/*** IP LAYER ***/
-
-static struct pkt_stats stat_ip_in, stat_ip_invalid, stat_ip_uninteresting, stat_ip_fragmented;
-
-static void ip_got_packet(struct pkt *p)
-{
-  struct iphdr *iph;
-
-  pkt_account(&stat_ip_in, p);
-  if (!(iph = pkt_peek(p, sizeof(*iph))))
-    goto invalid;
-  if (iph->ihl < 5)
-    goto invalid;
-  if (iph->version != 4)
-    goto invalid;
-  uns hdrlen = 4*iph->ihl;
-  if (pkt_len(p) < hdrlen)
-    goto invalid;
-  if (!tcpip_verify_checksum(tcpip_calc_checksum(p->data, hdrlen, 0)))
-    goto invalid;
-  uns len = ntohs(iph->tot_len);
-  if (len < hdrlen || len > pkt_len(p))
-    goto invalid;
-  pkt_unappend(p, pkt_len(p) - len);
-  pkt_pop(p, hdrlen);
-
-  if (iph->protocol != IPPROTO_TCP)
-    {
-      pkt_account(&stat_ip_uninteresting, p);
-      goto drop;
-    }
-  /* XXX: Fragmentation not supported yet, but well-behaved TCP stacks don't use it anyway */
-  if (ntohs(iph->frag_off) & 0x3fff)
-    {
-      pkt_account(&stat_ip_fragmented, p);
-      goto drop;
-    }
-  tcp_got_packet(iph, p);
-  return;
-
- invalid:
-  pkt_account(&stat_ip_invalid, p);
- drop:
-  pkt_free(p);
-}
-
-/*** LINK LAYER ***/
-
-static struct pkt_stats stat_link_dwarf, stat_link_in, stat_link_unknown, stat_link_arp;
-
-static void link_eth_got_packet(struct pkt *p)
-{
-  struct ether_header *eth;
-  uns etype;
-
-  pkt_account(&stat_link_in, p);
-  if (!(eth = pkt_pop(p, sizeof(*eth))))
-    {
-      pkt_account(&stat_link_dwarf, p);
-      return;
-    }
-  etype = ntohs(eth->ether_type);
-  switch (etype)
-    {
-    case ETHERTYPE_IP:
-      ip_got_packet(p);
-      break;
-    case ETHERTYPE_ARP:
-      pkt_account(&stat_link_arp, p);
-      pkt_free(p);
-      break;
-    default:
-      // printf("Unknown ethertype: %04x\n", etype);
-      pkt_account(&stat_link_unknown, p);
-      pkt_free(p);
-    }
-}
-
 /*** PCAP INTERFACE ***/
 
 static void (*link_handler)(struct pkt *);
@@ -209,31 +54,79 @@ static void got_pcap_packet(u_char *userdata UNUSED, const struct pcap_pkthdr *h
     }
   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;
   link_handler(p);
 }
 
+static void usage(void)
+{
+  fprintf(stderr, "Usage: netgrind [<switches>] <capture-file>\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\
+-w             TCP: Wait for ACK before processing packets\n\
+");
+  exit(1);
+}
+
 int main(int argc, char **argv)
 {
   char errbuf[PCAP_ERRBUF_SIZE];
   pcap_t *pcap;
-  int dlt;
-
-  if (argc != 2)
-    die("Usage: netgrind <capture-file>");
-
-  if (!(pcap = pcap_open_offline(argv[1], errbuf)))
-    die("Unable to open %s: %s", argv[1], errbuf);
+  int c, dlt;
+  int max_packets = -1;
+  byte *filter = NULL;
+  struct bpf_program filter_prog;
+
+  tcp_default_appl = &appl_sink;
+  while ((c = getopt(argc, argv, "ac:d:D:f:w")) >= 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 'w':
+       tcp_wait_for_ack = 1;
+       break;
+      default:
+       usage();
+      }
+  if (optind != argc - 1)
+    usage();
+
+  tcp_init();
+
+  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 (pcap_loop(pcap, -1, got_pcap_packet, NULL) < 0)
+  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);
+    }
+  if (pcap_loop(pcap, max_packets, got_pcap_packet, NULL) < 0)
     die("Capture failed: %s", pcap_geterr(pcap));
-#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
+  tcp_cleanup();
   printf("Pcap: %Ld(%Ld) incomplete\n",
         stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes);
   printf("Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
@@ -241,14 +134,20 @@ int main(int argc, char **argv)
         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\n",
+  printf("IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented, %Ld(%Ld) bad checksum; %d flows\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);
-  printf("TCP: %Ld(%Ld) in, %Ld(%Ld) invalid\n",
+        stat_ip_fragmented.packets, stat_ip_fragmented.bytes,
+        stat_ip_badsum.packets, stat_ip_badsum.bytes,
+        tcp_total_flows);
+  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_invalid.packets, stat_tcp_invalid.bytes,
+        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);
   pcap_close(pcap);
   return 0;
 }