]> git.tdb.fi Git - netvis.git/blob - source/netvis.cpp
Restructure packet handling
[netvis.git] / source / netvis.cpp
1 /* $Id$
2
3 This file is part of NetVis
4 Copyright @ 2008 Mikko Rasa, Mikkosoft Productions
5 Distributed unter the GPL
6 */
7
8 #include <limits>
9 #include <cstdlib>
10 #include <cmath>
11 #include <signal.h>
12 #include <msp/core/getopt.h>
13 #include <msp/debug/profilingscope.h>
14 #include <msp/gl/blend.h>
15 #include <msp/gl/framebuffer.h>
16 #include <msp/gl/immediate.h>
17 #include <msp/gl/matrix.h>
18 #include <msp/gl/misc.h>
19 #include <msp/gl/projection.h>
20 #include <msp/gl/texture2d.h>
21 #include <msp/gl/transform.h>
22 #include <msp/io/print.h>
23 #include <msp/strings/format.h>
24 #include <msp/time/units.h>
25 #include <msp/time/utils.h>
26 #include "history.h"
27 #include "host.h"
28 #include "netvis.h"
29 #include "packet.h"
30 #include "port.h"
31 #include "resolver.h"
32
33 using namespace std;
34 using namespace Msp;
35
36 NetVis::NetVis(int argc, char **argv):
37         pcap(0),
38         resolver(0),
39         wnd(0),
40         font(0),
41         max_hosts(1000),
42         max_visible_hosts(30),
43         frames(0)
44 {
45         if(argc<2)
46                 throw usage_error("No interface given");
47         iface = argv[1];
48
49         char err[1024];
50         pcap = pcap_open_live(iface.c_str(), 128, true, 0, err);
51         if(!pcap)
52                 throw runtime_error(err);
53
54         if(pcap_setnonblock(pcap, true, err)==-1)
55                 throw runtime_error(err);
56
57         pcap_lookupnet(iface.c_str(), &localnet, &localnet_mask, err);
58         localnet = ntohl(localnet);
59         localnet_mask = ntohl(localnet_mask);
60
61         resolver = new Resolver;
62
63         wnd = new Graphics::SimpleGLWindow(1024, 768);
64         wnd->set_title("NetVis");
65         wnd->signal_close.connect(sigc::bind(sigc::mem_fun(this, &NetVis::exit), 0));
66         wnd->show();
67
68         GL::Blend::alpha().bind();
69
70         font = new GL::Font;
71         DataFile::load(*font, "dejavu-10.font");
72
73         history = new History(*this, 301, 100);
74
75         catch_signal(SIGINT);
76 }
77
78 NetVis::~NetVis()
79 {
80         delete history;
81         delete resolver;
82
83         delete font;
84         delete wnd;
85
86         pcap_close(pcap);
87         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
88                 delete i->second;
89         for(map<unsigned, Host *>::iterator i=disabled_hosts.begin(); i!=disabled_hosts.end(); ++i)
90                 delete i->second;
91         for(map<unsigned, Port *>::iterator i=ports.begin(); i!=ports.end(); ++i)
92                 delete i->second;
93         for(list<Packet *>::iterator i=packets.begin(); i!=packets.end(); ++i)
94                 delete *i;
95 }
96
97 void NetVis::tick()
98 {
99         Msp::Time::TimeStamp t = Msp::Time::now();
100         Msp::Time::TimeDelta dt;
101         if(tick_t)
102                 dt = t-tick_t;
103         tick_t = t;
104
105         if(tick_t>fps_t+Msp::Time::sec)
106         {
107                 fps = frames/((tick_t-fps_t)/Msp::Time::sec);
108                 fps_t = tick_t;
109                 frames = 0;
110         }
111
112         wnd->get_display().tick();
113
114         while(pcap_dispatch(pcap, -1, &capture_handler, reinterpret_cast<unsigned char *>(this))>0) ;
115
116         resolver->tick();
117         history->tick(tick_t);
118
119
120         float min_activity = numeric_limits<float>::max();
121         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
122         {
123                 i->second->tick(dt);
124                 min_activity = min(min_activity, i->second->get_activity());
125         }
126         float del_limit = pow(10, 6-0.1*static_cast<int>(max_hosts-hosts.size()-disabled_hosts.size()));
127         for(map<unsigned, Host *>::iterator i=disabled_hosts.begin(); i!=disabled_hosts.end();)
128         {
129                 i->second->tick(dt);
130
131                 if(i->second->get_activity()>min_activity)
132                 {
133                         i->second->set_active(true);
134                         hosts.insert(*i);
135                         for(unsigned j=0; j<100; ++j)
136                                 i->second->tick(100*Time::msec);
137                         disabled_hosts.erase(i++);
138                 }
139                 else if(i->second->get_activity()<del_limit)
140                 {
141                         resolver->cancel(i->second);
142                         delete i->second;
143                         disabled_hosts.erase(i++);
144                 }
145                 else
146                         ++i;
147         }
148
149         if(hosts.size()>max_visible_hosts)
150         {
151                 list<float> activity;
152                 for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
153                         activity.push_back(i->second->get_activity());
154                 activity.sort();
155
156                 list<float>::iterator j = activity.begin();
157                 advance(j, activity.size()-max_visible_hosts);
158                 float limit = *j;
159
160                 for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end();)
161                 {
162                         if(i->second->get_activity()<limit)
163                         {
164                                 i->second->set_active(false);
165                                 disabled_hosts.insert(*i);
166                                 hosts.erase(i++);
167                         }
168                         else
169                                 ++i;
170                 }
171         }
172
173         for(map<unsigned, Port *>::iterator i=ports.begin(); i!=ports.end();)
174         {
175                 i->second->tick(dt);
176
177                 if(!i->second->is_registered() && i->second->get_activity()<0.1)
178                 {
179                         delete i->second;
180                         ports.erase(i++);
181                 }
182                 else
183                         ++i;
184         }
185
186         for(list<Packet *>::iterator i=packets.begin(); i!=packets.end();)
187         {
188                 (*i)->tick(dt);
189                 if((*i)->get_stale())
190                 {
191                         delete *i;
192                         i = packets.erase(i);
193                 }
194                 else
195                         ++i;
196         }
197
198         render();
199         wnd->swap_buffers();
200
201         ++frames;
202 }
203
204 void NetVis::render()
205 {
206         GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT);
207
208         GL::MatrixStack::projection() = GL::Matrix::ortho_centered(1024, 768);
209         GL::MatrixStack::modelview() = GL::Matrix();
210
211         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
212                 i->second->render();
213         {
214                 GL::Immediate imm((GL::COLOR4_UBYTE, GL::VERTEX2));
215                 imm.begin(GL::QUADS);
216                 for(list<Packet *>::iterator i=packets.begin(); i!=packets.end(); ++i)
217                         (*i)->render(imm);
218                 imm.end();
219         }
220
221         GL::MatrixStack::modelview() = GL::Matrix::translation(-500, 360, 0);
222         unsigned n = 0;
223         for(map<unsigned, Port *>::iterator i=ports.begin(); (i!=ports.end() && n<50); ++i)
224         {
225                 float act = i->second->get_activity();
226                 if((i->second->is_registered() && act>1) || act>200)
227                 {
228                         i->second->render();
229                         GL::MatrixStack::modelview() *= GL::Matrix::translation(0, -12, 0);
230                         ++n;
231                 }
232         }
233
234         GL::MatrixStack::modelview() = GL::Matrix::translation(-500, -348, 0);
235         GL::MatrixStack::modelview() *= GL::Matrix::scaling(10);
236         font->draw_string(format("%d hosts", hosts.size()+disabled_hosts.size()));
237         GL::MatrixStack::modelview() *= GL::Matrix::translation(0, -1.2, 0);
238         font->draw_string(format("%d ports", ports.size()));
239         GL::MatrixStack::modelview() *= GL::Matrix::translation(0, -1.2, 0);
240         font->draw_string(format("%.2f fps", fps));
241         GL::Texture::unbind();
242
243         GL::MatrixStack::modelview() = GL::Matrix::translation(170, -370, 0);
244         history->render();
245 }
246
247 Host &NetVis::get_host(unsigned a)
248 {
249         map<unsigned, Host *>::iterator i = hosts.find(a);
250         if(i!=hosts.end())
251                 return *i->second;
252
253         i = disabled_hosts.find(a);
254         if(i!=disabled_hosts.end())
255                 return *i->second;
256
257         Host *host = new Host(*this, a);
258         if((a&localnet_mask)==localnet)
259                 host->set_local(true);
260         resolver->push(host);
261         host->set_position(Vector2(rand()*400.0/RAND_MAX-200.0, rand()*400.0/RAND_MAX-200.0));
262         for(unsigned j=0; j<100; ++j)
263                 host->tick(100*Time::msec);
264         hosts[a] = host;
265         return *host;
266 }
267
268 Port &NetVis::get_port(unsigned number)
269 {
270         map<unsigned, Port *>::iterator i = ports.find(number);
271         if(i!=ports.end())
272                 return *i->second;
273         Port *port = new Port(*this, number);
274         ports[number] = port;
275         return *port;
276 }
277
278 void NetVis::capture_handler(unsigned char *user, const pcap_pkthdr *cap, const unsigned char *data)
279 {
280         NetVis *self = reinterpret_cast<NetVis *>(user);
281
282         CaptureContext ctx;
283         ctx.cap_hdr = cap;
284         const ethhdr *eth = reinterpret_cast<const ethhdr *>(data);
285         self->handle_ethernet(ctx, eth, cap->caplen);
286 }
287
288 void NetVis::handle_ethernet(CaptureContext &ctx, const ethhdr *eth, unsigned len)
289 {
290         ctx.size = ctx.cap_hdr->len-sizeof(ethhdr);
291
292         int proto = ntohs(eth->h_proto);
293         if(proto==ETH_P_IP)
294         {
295                 const iphdr *ip = reinterpret_cast<const iphdr *>(eth+1);
296                 handle_ipv4(ctx, ip, len-sizeof(ethhdr));
297         }
298         else
299                 IO::print("Unknown protocol in eth: %d\n", proto);
300 }
301
302 void NetVis::handle_ipv4(CaptureContext &ctx, const iphdr *ip, unsigned len)
303 {
304         ctx.src_host = &get_host(ntohl(ip->saddr));
305         if((ntohl(ip->daddr)&0xFF)!=0xFF)
306                 ctx.dst_host = &get_host(ntohl(ip->daddr));
307
308         if(ip->protocol==IPPROTO_TCP)
309         {
310                 const tcphdr *tcp = reinterpret_cast<const tcphdr *>(ip+1);
311                 handle_tcp(ctx, tcp, len-sizeof(iphdr));
312         }
313         else if(ip->protocol==IPPROTO_UDP)
314         {
315                 const udphdr *udp = reinterpret_cast<const udphdr *>(ip+1);
316                 handle_udp(ctx, udp, len-sizeof(iphdr));
317         }
318         else
319                 IO::print("Unknown protocol in ip: %d\n", ip->protocol);
320 }
321
322
323 void NetVis::handle_tcp(CaptureContext &ctx, const tcphdr *tcp, unsigned)
324 {
325         ctx.src_port = &get_port(ntohs(tcp->source));
326         ctx.dst_port = &get_port(ntohs(tcp->dest));
327         handle_packet(ctx);
328 }
329
330 void NetVis::handle_udp(CaptureContext &ctx, const udphdr *udp, unsigned)
331 {
332         ctx.src_port = &get_port(ntohs(udp->source));
333         ctx.dst_port = &get_port(ntohs(udp->dest));
334         handle_packet(ctx);
335 }
336
337 void NetVis::handle_packet(CaptureContext &ctx)
338 {
339         Port *port = 0;
340         if(ctx.src_port && ctx.dst_port)
341         {
342                 if(ctx.src_port->is_registered()!=ctx.dst_port->is_registered())
343                 {
344                         if(ctx.src_port->is_registered())
345                                 port = ctx.src_port;
346                         else
347                                 port = ctx.dst_port;
348                 }
349                 else if(ctx.src_port->get_number()<ctx.dst_port->get_number())
350                         port = ctx.src_port;
351                 else
352                         port = ctx.dst_port;
353         }
354         else
355                 port = &get_port(0);
356
357         float throttle = ctx.src_host->send_packet();
358         if(throttle<1)
359         {
360                 packets.push_back(new Packet(*ctx.src_host, ctx.dst_host, port->get_color(), ctx.size));
361                 packets.back()->tick(-throttle*Msp::Time::sec);
362         }
363
364         ctx.src_host->add_activity(ctx.size);
365         if(ctx.dst_host)
366                 ctx.dst_host->add_activity(ctx.size);
367
368         if(ctx.src_port)
369                 ctx.src_port->add_activity(ctx.size);
370         if(ctx.dst_port)
371                 ctx.dst_port->add_activity(ctx.size);
372
373         bool local_src = ctx.src_host->is_local();
374         bool local_dst = (ctx.dst_host && ctx.dst_host->is_local());
375         if(local_src && !local_dst)
376                 history->activity(0, ctx.size);
377         else if(local_dst && !local_src)
378                 history->activity(ctx.size, 0);
379 }
380
381 void NetVis::sighandler(int)
382 {
383         exit(0);
384 }
385
386
387 NetVis::CaptureContext::CaptureContext():
388         cap_hdr(0),
389         src_host(0),
390         src_port(0),
391         dst_host(0),
392         dst_port(0),
393         size(0)
394 { }