*/
#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 <getopt.h>
+#include <signal.h>
+#include <setjmp.h>
#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
#include <pcap.h>
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 volatile int stop_signal;
-static struct pkt_stats stat_tcp_in, stat_tcp_invalid;
-
-static void tcp_got_packet(struct iphdr *iph, struct pkt *p)
+static void sigint_handler(int arg UNUSED)
{
- 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);
+ stop_signal = 1;
}
-/*** IP LAYER ***/
-
-static struct pkt_stats stat_ip_in, stat_ip_invalid, stat_ip_uninteresting, stat_ip_fragmented;
+/*** MANUAL MULTIPLEXER ***/
-static void ip_got_packet(struct pkt *p)
+static void mux_open(struct flow *f, u64 when)
{
- 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);
+ 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);
}
-/*** 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);
- }
-}
+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)
{
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;
-
- 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);
- 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));
-#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",
+ int c, dlt;
+ struct bpf_program filter_prog;
+ byte *histogram = NULL;
+
+ 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);
+
+ 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\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);
- 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);
+ 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);
- pcap_close(pcap);
+ 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);
+ 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;
}