From: Martin Mares Date: Fri, 6 Jun 2003 15:57:32 +0000 (+0000) Subject: Initial revision X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=d9201a8d4c5093f1c8fef6d736674e26ef0db25d;p=netgrind.git Initial revision --- d9201a8d4c5093f1c8fef6d736674e26ef0db25d diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c7bf0d0 --- /dev/null +++ b/Makefile @@ -0,0 +1,80 @@ +CLANG=-std=gnu99 +COPT=-O2 -fstrict-aliasing -finline-limit=2000 +CWARNS=-Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Winline +LOPT= +LIBS= +CDEBUG=-DDEBUG -ggdb + +CFLAGS=$(CLANG) $(COPT) $(CDEBUG) $(CWARNS) -I. +LDFLAGS=$(LOPT) + +.PHONY: all dust clean distclean programs dirtree + +DIRS= +PROGS= + +all: dirtree programs + +dust:: + rm -f `find . -path "*~" -or -name "\#*\#" -or -name core` + +clean:: dust + rm -rf obj + rm -f depend depend.new TAGS + +distclean:: clean + +include lib/Makefile +include netgrind/Makefile + +programs: $(PROGS) + +dirtree: $(addsuffix /.dir-stamp,$(addprefix obj/,$(DIRS))) + +tags: + etags `find . -name "*.[ch]"` + +# Black magic with dependencies. It would be more correct to make "depend.new" +# a prerequisite for "depend", but "depend.new" often has the same timestamp +# as "depend" which would confuse make a lot and either force remaking anyway +# or (as in current versions of GNU make) erroneously skipping the remaking. + +-include depend + +depend: force + if [ -s depend.new ] ; then build/mergedeps depend depend.new ; >depend.new ; fi + +force: + +# Implicit rules + +%.dir-stamp: + mkdir -p $(@D) && touch $@ + +%.a: + ar rcs $@ $? + +obj/%.o: %.c + DEPENDENCIES_OUTPUT="depend.new $@" $(CC) $(CFLAGS) -c -o $@ $< + +obj/%-tt.o: %.c + DEPENDENCIES_OUTPUT="depend.new $@" $(CC) $(CFLAGS) -DTEST -c -o $@ $< + +obj/%-t: obj/%-tt.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +obj/%: obj/%.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +obj/%: %.sh + cp $(TOPDIR)/$^ $@ + chmod +x $@ + +obj/%: %.pl + cp $(TOPDIR)/$^ $@ + chmod +x $@ + +# Don't delete intermediate targets. There shouldn't be any, but due to bugs +# in GNU Make rules with targets in not-yet-existing directories are ignored +# when searching for implicit rules and thence targets considered intermediate. +.SECONDARY: diff --git a/build/mergedeps b/build/mergedeps new file mode 100755 index 0000000..e1c467d --- /dev/null +++ b/build/mergedeps @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +@ARGV == 2 or die "Usage: mergedeps "; +foreach $a (@ARGV) { + open F, "$a" or next; + $t = ""; + while () { + $t .= $_; + if (! /\\$/) { + ($t =~ /^(.*):/) || die "Parse error at $t"; + $rules{$1} = $t; + $t = ""; + } + } + close F; +} +open(F,">" . $ARGV[0]) || die "Unable to write output file"; +foreach $a (sort keys %rules) { + print F $rules{$a}; +} +close F; diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..c5190fd --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,7 @@ +DIRS+=lib + +LIBSH=obj/lib/libsh.a +LIBSH_MODS=alloc assert +LIBSH_MOD_PATHS=$(addprefix obj/lib/,$(LIBSH_MODS)) + +$(LIBSH): $(addsuffix .o,$(LIBSH_MOD_PATHS)) diff --git a/lib/alloc.c b/lib/alloc.c new file mode 100644 index 0000000..40f3edf --- /dev/null +++ b/lib/alloc.c @@ -0,0 +1,34 @@ +/* + * Sherlock Library -- Memory Allocation + * + * (c) 2000 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#include "lib/lib.h" + +#include +#include + +#ifndef DMALLOC + +void * +xmalloc(uns size) +{ + void *x = malloc(size); + if (!x) + die("Cannot allocate %d bytes of memory", size); + return x; +} + +#endif + +void * +xmalloc_zero(uns size) +{ + void *x = xmalloc(size); + bzero(x, size); + return x; +} diff --git a/lib/assert.c b/lib/assert.c new file mode 100644 index 0000000..61bd61b --- /dev/null +++ b/lib/assert.c @@ -0,0 +1,27 @@ +/* + * Sherlock Library -- Assertions + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#include "lib/lib.h" + +#include + +#ifdef DEBUG +void +assert_failed(char *assertion, char *file, int line) +{ + die("Assertion `%s' failed at %s:%d", assertion, file, line); + abort(); +} +#else +void +assert_failed(void) +{ + die("Internal error: Assertion failed."); +} +#endif diff --git a/lib/config.h b/lib/config.h new file mode 100644 index 0000000..00e9f77 --- /dev/null +++ b/lib/config.h @@ -0,0 +1,55 @@ +/* + * Sherlock Library -- Configuration-Dependent Definitions + * + * (c) 1997--2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _SHERLOCK_CONFIG_H +#define _SHERLOCK_CONFIG_H + +#define CPU_I386 1 +#define CPU_LITTLE_ENDIAN 1 +#define CPU_ALLOW_UNALIGNED 1 +#define CPU_STRUCT_ALIGN 4 +#define CONFIG_LINUX 1 + +/* Types */ + +typedef unsigned char byte; /* exactly 8 bits, unsigned */ +typedef signed char sbyte; /* exactly 8 bits, signed */ +typedef unsigned short word; /* exactly 16 bits, unsigned */ +typedef short sword; /* exactly 16 bits, signed */ +typedef unsigned short u16; /* exactly 16 bits, unsigned */ +typedef short s16; /* exactly 16 bits, signed */ +typedef unsigned int u32; /* exactly 32 bits, unsigned */ +typedef int s32; /* exactly 32 bits, signed */ +typedef unsigned int uns; /* at least 32 bits */ +typedef unsigned long long int u64; /* exactly 64 bits, unsigned */ +typedef long long int s64; /* exactly 64 bits, signed */ +typedef unsigned long addr_int_t; /* Both integer and address */ +typedef unsigned int sh_time_t; /* Timestamp */ + +#ifndef NULL +#define NULL (void *)0 +#endif + +/* Misc */ + +#ifdef __GNUC__ + +#undef inline +#define NONRET __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) +#define CONSTRUCTOR __attribute__((constructor)) +#define PACKED __attribute__((packed)) +#define CONST __attribute__((const)) +#define PURE __attribute__((const)) + +#else +#error This program requires the GNU C compiler. +#endif + +#endif diff --git a/lib/lib.h b/lib/lib.h new file mode 100644 index 0000000..13c07d6 --- /dev/null +++ b/lib/lib.h @@ -0,0 +1,86 @@ +/* + * Sherlock Library -- Miscellaneous Functions + * + * (c) 1997--2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +/* + * This file should be included as the very first include in all + * source files, especially before all OS includes since it sets + * up libc feature macros. + */ + +#ifndef _SHERLOCK_LIB_H +#define _SHERLOCK_LIB_H + +#include "lib/config.h" + +/* Tell libc we're going to use all extensions available */ + +#define _GNU_SOURCE + +/* Ugly structure handling macros */ + +#define OFFSETOF(s, i) ((unsigned int)&((s *)0)->i) +#define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i))) +#define ALIGN(s, a) (((s)+a-1)&~(a-1)) +#define UNALIGNED_PART(ptr, type) (((long) (ptr)) % sizeof(type)) + +/* Some other macros */ + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define CLAMP(x,min,max) ({ int _t=x; (_t < min) ? min : (_t > max) ? max : _t; }) +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) + +/* Logging */ + +void die(byte *, ...) NONRET; + +#ifdef DEBUG +void assert_failed(char *assertion, char *file, int line) NONRET; +#define ASSERT(x) do { if (!(x)) assert_failed(#x, __FILE__, __LINE__); } while(0) +#else +void assert_failed(void) NONRET; +#define ASSERT(x) do { if (__builtin_constant_p(x) && !(x)) assert_failed(); } while(0) +#endif + +#ifdef LOCAL_DEBUG +#define DBG(x,y...) log(L_DEBUG, x,##y) +#else +#define DBG(x,y...) do { } while(0) +#endif + +/* Memory allocation */ + +#ifdef DMALLOC +/* + * The standard dmalloc macros tend to produce lots of namespace + * conflicts and we use only xmalloc and xfree, so we can define + * the stubs ourselves. + */ +#define DMALLOC_DISABLE +#include +#define xmalloc(size) _xmalloc_leap(__FILE__, __LINE__, size) +#define xrealloc(ptr,size) _xrealloc_leap(__FILE__, __LINE__, ptr, size) +#define xfree(ptr) _xfree_leap(__FILE__, __LINE__, ptr) +#else +/* + * Unfortunately, several libraries we might want to link to define + * their own xmalloc and we don't want to interfere with them, hence + * the renaming. + */ +#define xmalloc sh_xmalloc +void *xmalloc(unsigned); +void *xrealloc(void *, unsigned); +#define xfree(x) free(x) +#endif + +void *xmalloc_zero(unsigned); +byte *stralloc(byte *); + +#endif diff --git a/lib/lists.h b/lib/lists.h new file mode 100644 index 0000000..2f7cea1 --- /dev/null +++ b/lib/lists.h @@ -0,0 +1,66 @@ +/* + * Sherlock Library -- Linked Lists + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _SHERLOCK_LISTS_H +#define _SHERLOCK_LISTS_H + +typedef struct node { + struct node *next, *prev; +} node; + +typedef struct list { + struct node head; +} list; + +static inline void *list_first(list *l) +{ + return (l->head.next != &l->head) ? l->head.next : NULL; +} + +static inline void *list_last(list *l) +{ + return (l->head.prev != &l->head) ? l->head.prev : NULL; +} + +#define WALK_LIST(n,list) for(n=(void*)(list).head; ((node*)(n))->next != &(list).head; n=(void*)((node*)(n))->next) + +static inline void list_insert(node *what, node *after) +{ + node *before = after->next; + what->next = before; + what->prev = before->prev; + before->prev = what; + after->next = what; +} + +static inline void list_add_tail(list *l, node *n) +{ + list_insert(n, l->head.prev); +} + +static inline void list_add_head(list *l, node *n) +{ + list_insert(n, &l->head); +} + +static inline void list_remove(node *n) +{ + node *before = n->prev; + node *after = n->next; + before->next = after; + after->prev = before; +} + +static inline void list_init(list *l) +{ + node *head = &l->head; + head->next = head->prev = head; +} + +#endif diff --git a/netgrind/Makefile b/netgrind/Makefile new file mode 100644 index 0000000..60ce104 --- /dev/null +++ b/netgrind/Makefile @@ -0,0 +1,6 @@ +DIRS+=netgrind +PROGS+=obj/netgrind/netgrind +NG_OBJS=netgrind.o pkt.o + +obj/netgrind/netgrind: $(addprefix obj/netgrind/,$(NG_OBJS)) $(LIBSH) +obj/netgrind/netgrind: LIBS+=-lpcap diff --git a/netgrind/netgrind.c b/netgrind/netgrind.c new file mode 100644 index 0000000..a25f02a --- /dev/null +++ b/netgrind/netgrind.c @@ -0,0 +1,254 @@ +/* + * Netgrind -- The Network Traffic Analyser + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include "lib/lib.h" +#include "netgrind/netgrind.h" +#include "netgrind/pkt.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +void die(byte *msg, ...) +{ + va_list args; + + va_start(args, msg); + fputs("netgrind: ", stderr); + vfprintf(stderr, msg, args); + fputs("\n", stderr); + 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 *); +static struct pkt_stats stat_pcap_incomplete; + +static int link_setup_handler(int dlt) +{ + switch (dlt) + { + case DLT_EN10MB: link_handler = link_eth_got_packet; return 1; + default: return 0; + } +} + +static void got_pcap_packet(u_char *userdata UNUSED, const struct pcap_pkthdr *hdr, const u_char *pkt) +{ + if (hdr->caplen != hdr->len) + { + stat_pcap_incomplete.packets++; + stat_pcap_incomplete.bytes += hdr->len - hdr->caplen; + return; + } + struct pkt *p = pkt_new(0, hdr->len); + memcpy(pkt_append(p, hdr->len), pkt, hdr->len); + link_handler(p); +} + +int main(int argc, char **argv) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + pcap_t *pcap; + int dlt; + + if (argc != 2) + die("Usage: netgrind "); + + 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", + stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes); + 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", + 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_tcp_in.packets, stat_tcp_in.bytes, + stat_tcp_invalid.packets, stat_tcp_invalid.bytes); + pcap_close(pcap); + return 0; +} diff --git a/netgrind/netgrind.h b/netgrind/netgrind.h new file mode 100644 index 0000000..60512ca --- /dev/null +++ b/netgrind/netgrind.h @@ -0,0 +1,8 @@ +/* + * Netgrind -- The Network Traffic Analyser + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ diff --git a/netgrind/pkt.c b/netgrind/pkt.c new file mode 100644 index 0000000..ad0c21c --- /dev/null +++ b/netgrind/pkt.c @@ -0,0 +1,27 @@ +/* + * Netgrind -- Packet Buffers + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include "lib/lib.h" +#include "netgrind/pkt.h" + +#include + +struct pkt *pkt_new(uns preroom, uns postroom) +{ + uns len = preroom + postroom; + struct pkt *p = xmalloc(sizeof(struct pkt) + len); + p->stop = p->data = p->buf + preroom; + p->ebuf = p->buf + len; + return p; +} + +void pkt_free(struct pkt *p) +{ + xfree(p); +} diff --git a/netgrind/pkt.h b/netgrind/pkt.h new file mode 100644 index 0000000..c1e4a99 --- /dev/null +++ b/netgrind/pkt.h @@ -0,0 +1,73 @@ +/* + * Netgrind -- Packet Buffers + * + * (c) 2003 Martin Mares + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include "lib/lists.h" + +struct pkt { + struct node n; + u64 timestamp; + byte *data, *stop, *ebuf; + byte buf[0]; +}; + +static inline uns pkt_len(struct pkt *p) +{ + return p->stop - p->data; +} + +static inline void pkt_push(struct pkt *p, uns len) +{ + p->data -= len; + ASSERT(p->data >= p->buf); +} + +static inline void *pkt_pop(struct pkt *p, uns len) +{ + byte *d = p->data; + if (d + len > p->stop) + return NULL; + p->data += len; + return d; +} + +static inline void *pkt_peek(struct pkt *p, uns len) +{ + byte *d = p->data; + if (d + len > p->stop) + return NULL; + return d; +} + +static inline byte *pkt_append(struct pkt *p, uns len) +{ + byte *d = p->stop; + p->stop = d + len; + ASSERT(p->stop <= p->ebuf); + return d; +} + +static inline void pkt_unappend(struct pkt *p, uns len) +{ + p->stop -= len; + ASSERT(p->stop >= p->data); +} + +struct pkt *pkt_new(uns preroom, uns postroom); +void pkt_free(struct pkt *pkt); + +struct pkt_stats { + u64 packets; + u64 bytes; +}; + +static inline void pkt_account(struct pkt_stats *stats, struct pkt *pkt) +{ + stats->packets++; + stats->bytes += pkt_len(pkt); +}