]> mj.ucw.cz Git - netgrind.git/blob - netgrind/netgrind.c
Initial revision
[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/netgrind.h"
12 #include "netgrind/pkt.h"
13
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <net/ethernet.h>
19 #include <netinet/in.h>
20 #include <netinet/ip.h>
21 #include <netinet/tcp.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 /*** CHECKSUMMING ***/
37
38 static uns tcpip_calc_checksum(void *data, uns len, uns csum)
39 {
40   byte *x = data;
41
42   while (len >= 2)
43     {
44       csum += (x[0] << 8) | x[1];
45       if (csum & 0xffff0000)
46         {
47           csum &= 0x0000ffff;
48           csum++;
49         }
50       x += 2;
51       len -= 2;
52     }
53   if (len)
54     {
55       csum += x[0];
56       if (csum & 0xffff0000)
57         {
58           csum &= 0x0000ffff;
59           csum++;
60         }
61     }
62   return csum;
63 }
64
65 static inline uns tcpip_verify_checksum(uns csum)
66 {
67   return (csum == 0xffff);
68 }
69
70 /*** TCP LAYER ***/
71
72 static struct pkt_stats stat_tcp_in, stat_tcp_invalid;
73
74 static void tcp_got_packet(struct iphdr *iph, struct pkt *p)
75 {
76   struct tcphdr *tcph;
77   struct {
78     u32 src;
79     u32 dst;
80     byte zero;
81     byte proto;
82     u16 len;
83   } fakehdr;
84
85   pkt_account(&stat_tcp_in, p);
86   if (!(tcph = pkt_peek(p, sizeof(*tcph))))
87     goto invalid;
88   uns hdrlen = 4*tcph->doff;
89   if (hdrlen < sizeof(*tcph) || hdrlen > pkt_len(p))
90     goto invalid;
91   fakehdr.src = iph->saddr;
92   fakehdr.dst = iph->daddr;
93   fakehdr.zero = 0;
94   fakehdr.proto = IPPROTO_TCP;
95   fakehdr.len = htons(pkt_len(p));
96   uns sum = tcpip_calc_checksum(&fakehdr, sizeof(fakehdr), 0);
97   sum = tcpip_calc_checksum(p->data, pkt_len(p), sum);
98   if (!tcpip_verify_checksum(sum))
99     goto invalid;
100
101   /* Here we should do something with the packet */
102   pkt_free(p);
103   return;
104
105  invalid:
106   pkt_account(&stat_tcp_invalid, p);
107   pkt_free(p);
108 }
109
110 /*** IP LAYER ***/
111
112 static struct pkt_stats stat_ip_in, stat_ip_invalid, stat_ip_uninteresting, stat_ip_fragmented;
113
114 static void ip_got_packet(struct pkt *p)
115 {
116   struct iphdr *iph;
117
118   pkt_account(&stat_ip_in, p);
119   if (!(iph = pkt_peek(p, sizeof(*iph))))
120     goto invalid;
121   if (iph->ihl < 5)
122     goto invalid;
123   if (iph->version != 4)
124     goto invalid;
125   uns hdrlen = 4*iph->ihl;
126   if (pkt_len(p) < hdrlen)
127     goto invalid;
128   if (!tcpip_verify_checksum(tcpip_calc_checksum(p->data, hdrlen, 0)))
129     goto invalid;
130   uns len = ntohs(iph->tot_len);
131   if (len < hdrlen || len > pkt_len(p))
132     goto invalid;
133   pkt_unappend(p, pkt_len(p) - len);
134   pkt_pop(p, hdrlen);
135
136   if (iph->protocol != IPPROTO_TCP)
137     {
138       pkt_account(&stat_ip_uninteresting, p);
139       goto drop;
140     }
141   /* XXX: Fragmentation not supported yet, but well-behaved TCP stacks don't use it anyway */
142   if (ntohs(iph->frag_off) & 0x3fff)
143     {
144       pkt_account(&stat_ip_fragmented, p);
145       goto drop;
146     }
147   tcp_got_packet(iph, p);
148   return;
149
150  invalid:
151   pkt_account(&stat_ip_invalid, p);
152  drop:
153   pkt_free(p);
154 }
155
156 /*** LINK LAYER ***/
157
158 static struct pkt_stats stat_link_dwarf, stat_link_in, stat_link_unknown, stat_link_arp;
159
160 static void link_eth_got_packet(struct pkt *p)
161 {
162   struct ether_header *eth;
163   uns etype;
164
165   pkt_account(&stat_link_in, p);
166   if (!(eth = pkt_pop(p, sizeof(*eth))))
167     {
168       pkt_account(&stat_link_dwarf, p);
169       return;
170     }
171   etype = ntohs(eth->ether_type);
172   switch (etype)
173     {
174     case ETHERTYPE_IP:
175       ip_got_packet(p);
176       break;
177     case ETHERTYPE_ARP:
178       pkt_account(&stat_link_arp, p);
179       pkt_free(p);
180       break;
181     default:
182       // printf("Unknown ethertype: %04x\n", etype);
183       pkt_account(&stat_link_unknown, p);
184       pkt_free(p);
185     }
186 }
187
188 /*** PCAP INTERFACE ***/
189
190 static void (*link_handler)(struct pkt *);
191 static struct pkt_stats stat_pcap_incomplete;
192
193 static int link_setup_handler(int dlt)
194 {
195   switch (dlt)
196     {
197     case DLT_EN10MB:    link_handler = link_eth_got_packet; return 1;
198     default:            return 0;
199     }
200 }
201
202 static void got_pcap_packet(u_char *userdata UNUSED, const struct pcap_pkthdr *hdr, const u_char *pkt)
203 {
204   if (hdr->caplen != hdr->len)
205     {
206       stat_pcap_incomplete.packets++;
207       stat_pcap_incomplete.bytes += hdr->len - hdr->caplen;
208       return;
209     }
210   struct pkt *p = pkt_new(0, hdr->len);
211   memcpy(pkt_append(p, hdr->len), pkt, hdr->len);
212   link_handler(p);
213 }
214
215 int main(int argc, char **argv)
216 {
217   char errbuf[PCAP_ERRBUF_SIZE];
218   pcap_t *pcap;
219   int dlt;
220
221   if (argc != 2)
222     die("Usage: netgrind <capture-file>");
223
224   if (!(pcap = pcap_open_offline(argv[1], errbuf)))
225     die("Unable to open %s: %s", argv[1], errbuf);
226   dlt = pcap_datalink(pcap);
227   if (!link_setup_handler(dlt))
228     die("Don't know how to handle data link type %d", dlt);
229   if (pcap_loop(pcap, -1, got_pcap_packet, NULL) < 0)
230     die("Capture failed: %s", pcap_geterr(pcap));
231 #if 0
232   struct pcap_stat stats;
233   if (pcap_stats(pcap, &stats))
234     die("pcap_stats: %s", pcap_geterr(pcap));
235   printf("libpcap stats: %d packets received, %d dropped\n", stats.ps_recv, stats.ps_drop);
236 #endif
237   printf("Pcap: %Ld(%Ld) incomplete\n",
238          stat_pcap_incomplete.packets, stat_pcap_incomplete.bytes);
239   printf("Link: %Ld(%Ld) in, %Ld(%Ld) dwarves, %Ld(%Ld) strangers, %Ld(%Ld) ARPs\n",
240          stat_link_in.packets, stat_link_in.bytes,
241          stat_link_dwarf.packets, stat_link_dwarf.bytes,
242          stat_link_unknown.packets, stat_link_unknown.bytes,
243          stat_link_arp.packets, stat_link_arp.bytes);
244   printf("IP: %Ld(%Ld) in, %Ld(%Ld) invalid, %Ld(%Ld) boring, %Ld(%Ld) fragmented\n",
245          stat_ip_in.packets, stat_ip_in.bytes,
246          stat_ip_invalid.packets, stat_ip_invalid.bytes,
247          stat_ip_uninteresting.packets, stat_ip_uninteresting.bytes,
248          stat_ip_fragmented.packets, stat_ip_fragmented.bytes);
249   printf("TCP: %Ld(%Ld) in, %Ld(%Ld) invalid\n",
250          stat_tcp_in.packets, stat_tcp_in.bytes,
251          stat_tcp_invalid.packets, stat_tcp_invalid.bytes);
252   pcap_close(pcap);
253   return 0;
254 }