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