]> git.tdb.fi Git - netvis.git/blob - source/netvis.cpp
cece749e9be6a2ab6003e5d02447673a5ad2d983
[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 <netinet/ip.h>
13 #include <netinet/tcp.h>
14 #include <netinet/udp.h>
15 #include <linux/if_ether.h>
16 #include <msp/core/except.h>
17 #include <msp/debug/profilingscope.h>
18 #include <msp/gl/blend.h>
19 #include <msp/gl/framebuffer.h>
20 #include <msp/gl/immediate.h>
21 #include <msp/gl/matrix.h>
22 #include <msp/gl/misc.h>
23 #include <msp/gl/projection.h>
24 #include <msp/gl/texture2d.h>
25 #include <msp/gl/transform.h>
26 #include <msp/strings/formatter.h>
27 #include <msp/time/units.h>
28 #include <msp/time/utils.h>
29 #include "host.h"
30 #include "netvis.h"
31 #include "packet.h"
32 #include "port.h"
33 #include "resolver.h"
34
35 using namespace std;
36 using namespace Msp;
37
38 NetVis::NetVis(int argc, char **argv):
39         pcap(0),
40         resolver(0),
41         wnd(0),
42         font(0),
43         max_hosts(1000),
44         max_visible_hosts(30),
45         draw_labels(true),
46         blend(true),
47         frames(0)
48 {
49         if(argc<2)
50                 throw UsageError("No interface given");
51         iface = argv[1];
52
53         char err[1024];
54         pcap = pcap_open_live(iface.c_str(), 128, true, 0, err);
55         if(!pcap)
56                 throw Exception(err);
57
58         if(pcap_setnonblock(pcap, true, err)==-1)
59                 throw Exception(err);
60
61         pcap_lookupnet(iface.c_str(), &localnet, &localnet_mask, err);
62         localnet = ntohl(localnet);
63         localnet_mask = ntohl(localnet_mask);
64
65         resolver = new Resolver;
66
67         wnd = new Graphics::SimpleGLWindow(1024, 768);
68         wnd->set_title("NetVis");
69         wnd->signal_close.connect(sigc::bind(sigc::mem_fun(this, &NetVis::exit), 0));
70         wnd->signal_key_press.connect(sigc::mem_fun(this, &NetVis::key_press));
71         wnd->show();
72
73         GL::enable(GL::BLEND);
74         GL::blend_func(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
75
76         font = new GL::Font;
77         DataFile::load(*font, "dejavu-10.font");
78
79         catch_signal(SIGINT);
80 }
81
82 NetVis::~NetVis()
83 {
84         delete resolver;
85
86         delete font;
87         delete wnd;
88
89         pcap_close(pcap);
90         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
91                 delete i->second;
92         for(map<unsigned, Host *>::iterator i=disabled_hosts.begin(); i!=disabled_hosts.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         {
116                 Debug::ProfilingScope s(profiler, "capture");
117                 while(pcap_dispatch(pcap, -1, &capture_handler, reinterpret_cast<unsigned char *>(this))>0) ;
118         }
119
120         {
121                 Debug::ProfilingScope s(profiler, "tick");
122
123                 resolver->tick();
124
125                 float min_activity = numeric_limits<float>::max();
126                 for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
127                 {
128                         i->second->tick(dt);
129                         min_activity = min(min_activity, i->second->get_activity());
130                 }
131                 float del_limit = pow(10, 6-0.1*(max_hosts-hosts.size()-disabled_hosts.size()));
132                 for(map<unsigned, Host *>::iterator i=disabled_hosts.begin(); i!=disabled_hosts.end();)
133                 {
134                         i->second->tick(dt);
135
136                         if(i->second->get_activity()>min_activity)
137                         {
138                                 i->second->set_active(true);
139                                 hosts.insert(*i);
140                                 disabled_hosts.erase(i++);
141                         }
142                         else if(i->second->get_activity()<del_limit)
143                         {
144                                 delete i->second;
145                                 disabled_hosts.erase(i++);
146                         }
147                         else
148                                 ++i;
149                 }
150
151                 if(hosts.size()>max_visible_hosts)
152                 {
153                         list<float> activity;
154                         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
155                                 activity.push_back(i->second->get_activity());
156                         activity.sort();
157
158                         list<float>::iterator j = activity.begin();
159                         advance(j, activity.size()-max_visible_hosts);
160                         float limit = *j;
161
162                         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end();)
163                         {
164                                 if(i->second->get_activity()<limit)
165                                 {
166                                         i->second->set_active(false);
167                                         disabled_hosts.insert(*i);
168                                         hosts.erase(i++);
169                                 }
170                                 else
171                                         ++i;
172                         }
173                 }
174
175                 for(list<Packet *>::iterator i=packets.begin(); i!=packets.end();)
176                 {
177                         (*i)->tick(dt);
178                         if((*i)->get_stale())
179                         {
180                                 delete *i;
181                                 i = packets.erase(i);
182                         }
183                         else
184                                 ++i;
185                 }
186         }
187
188         {
189                 Debug::ProfilingScope s(profiler, "render");
190                 GL::clear(GL::COLOR_BUFFER_BIT);
191
192                 GL::matrix_mode(GL::PROJECTION);
193                 GL::load_identity();
194                 GL::ortho_centered(1024, 768);
195                 GL::matrix_mode(GL::MODELVIEW);
196                 GL::load_identity();
197
198                 for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
199                         i->second->render();
200                 if(draw_labels)
201                 {
202                         for(map<unsigned, Host *>::iterator i=hosts.begin(); i!=hosts.end(); ++i)
203                                 i->second->render_label();
204                         GL::Texture::unbind();
205                 }
206                 GL::Immediate imm((GL::COLOR4_UBYTE, GL::VERTEX2));
207                 imm.begin(GL::QUADS);
208                 for(list<Packet *>::iterator i=packets.begin(); i!=packets.end(); ++i)
209                         (*i)->render(imm);
210                 imm.end();
211
212                 GL::push_matrix();
213                 GL::translate(-500, 360, 0);
214                 unsigned n = 0;
215                 for(map<unsigned, Port>::iterator i=ports.begin(); (i!=ports.end() && n<50); ++i, ++n)
216                 {
217                         const GL::Color &color = i->second.get_color();
218
219                         imm.begin(GL::QUADS);
220                         imm.color(color.r, color.g, color.b, color.a);
221                         for(unsigned x=0; x<=4; x+=2)
222                         {
223                                 imm.vertex(x+0, 0);
224                                 imm.vertex(x+10, 0);
225                                 imm.vertex(x+10, 10);
226                                 imm.vertex(x+0, 10);
227                         }
228                         imm.end();
229
230                         GL::translate(0, -12, 0);
231                 }
232                 GL::pop_matrix();
233
234                 GL::push_matrix();
235                 if(draw_labels)
236                 {
237                         GL::push_matrix();
238                         GL::translate(-484, 361, 0);
239                         GL::scale_uniform(10);
240                         n = 0;
241                         for(map<unsigned, Port>::iterator i=ports.begin(); (i!=ports.end() && n<50); ++i, ++n)
242                         {
243                                 GL::Immediate imm2((GL::COLOR4_UBYTE, GL::TEXCOORD2, GL::VERTEX2));
244                                 imm.color(1.0f, 1.0f, 1.0f);
245                                 font->draw_string(i->second.get_name(), imm2);
246
247                                 GL::translate(0, -1.2, 0);
248                         }
249                         GL::pop_matrix();
250                         GL::Texture::unbind();
251                 }
252                 GL::pop_matrix();
253
254                 GL::push_matrix();
255                 GL::translate(-500, -360, 0);
256                 GL::scale_uniform(10);
257                 font->draw_string(format("%d hosts", hosts.size()+disabled_hosts.size()));
258                 GL::translate(0, -1.2, 0);
259                 font->draw_string(format("%.2f fps", fps));
260                 GL::pop_matrix();
261                 GL::Texture::unbind();
262
263                 wnd->swap_buffers();
264         }
265
266         ++frames;
267 }
268
269 Host &NetVis::get_host(unsigned a)
270 {
271         map<unsigned, Host *>::iterator i = hosts.find(a);
272         if(i!=hosts.end())
273                 return *i->second;
274
275         i = disabled_hosts.find(a);
276         if(i!=disabled_hosts.end())
277                 return *i->second;
278
279         Host *host = new Host(*this, a);
280         if((a&localnet_mask)==localnet)
281                 host->set_local(true);
282         resolver->push(host);
283         host->set_position(Vector2(rand()*400.0/RAND_MAX-200.0, rand()*400.0/RAND_MAX-200.0));
284         hosts[a] = host;
285         return *host;
286 }
287
288 GL::Color NetVis::generate_color(bool privileged) const
289 {
290         GL::Color color;
291         color.r = rand()*1.0/RAND_MAX;
292         color.g = rand()*1.0/RAND_MAX;
293         color.b = rand()*1.0/RAND_MAX;
294         float high = max(max(color.r, color.g), color.b);
295         color = color*(1.0/high);
296         if(privileged)
297         {
298                 float low = min(min(color.r, color.g), color.b);
299                 color = (color+-low)*(1/(1-low));
300         }
301         else
302                 color = color*0.6+0.4;
303         return color;
304 }
305
306 const Port &NetVis::get_port(unsigned number)
307 {
308         map<unsigned, Port>::iterator i = ports.find(number);
309         if(i!=ports.end())
310                 return i->second;
311
312         GL::Color best_color;
313         float best_score = 0;
314         for(unsigned j=0; (j<100 && best_score<1); ++j)
315         {
316                 GL::Color color = generate_color(number<1024);
317
318                 float score = 2;
319                 for(i=ports.begin(); i!=ports.end(); ++i)
320                 {
321                         const GL::Color &other = i->second.get_color();
322                         float dr = color.r-other.r;
323                         float dg = color.g-other.g;
324                         float db = color.b-other.b;
325                         score = min(score, dr*dr+dg*dg+db*db);
326                 }
327                 if(score>best_score)
328                 {
329                         best_score = score;
330                         best_color = color;
331                 }
332         }
333         best_color.a = 0.4f;
334         i = ports.insert(map<unsigned, Port>::value_type(number, Port(number, best_color))).first;
335         return i->second;
336 }
337
338 void NetVis::key_press(unsigned key, unsigned, wchar_t)
339 {
340         if(key==46)
341                 draw_labels = !draw_labels;
342         else if(key==56)
343         {
344                 blend = !blend;
345                 GL::set(GL_BLEND, blend);
346         }
347 }
348
349 void NetVis::capture_handler(unsigned char *user, const pcap_pkthdr *, const unsigned char *data)
350 {
351         NetVis *self = reinterpret_cast<NetVis *>(user);
352
353         const ethhdr *eth = reinterpret_cast<const ethhdr *>(data);
354         if(ntohs(eth->h_proto)==ETH_P_IP)
355         {
356                 const iphdr *ip = reinterpret_cast<const iphdr *>(eth+1);
357
358                 unsigned size = ntohs(ip->tot_len);
359                 unsigned port = 0;
360                 if(ip->protocol==IPPROTO_TCP)
361                 {
362                         const tcphdr *tcp = reinterpret_cast<const tcphdr *>(ip+1);
363                         port = min(ntohs(tcp->source), ntohs(tcp->dest));
364                 }
365                 else if(ip->protocol==IPPROTO_UDP)
366                 {
367                         const udphdr *udp = reinterpret_cast<const udphdr *>(ip+1);
368                         port = min(ntohs(udp->source), ntohs(udp->dest));
369                 }
370                 Host &shost = self->get_host(ntohl(ip->saddr));
371                 Host *dhost = 0;
372                 if((ntohl(ip->daddr)&0xFF)!=0xFF)
373                         dhost = &self->get_host(ntohl(ip->daddr));
374
375                 float throttle = shost.send_packet();
376                 if(throttle<1)
377                 {
378                         self->packets.push_back(new Packet(shost, dhost, self->get_port(port).get_color(), size));
379                         self->packets.back()->tick(-throttle*Msp::Time::sec);
380                 }
381
382                 shost.add_activity(size);
383                 if(dhost)
384                         dhost->add_activity(size);
385         }
386 }
387
388 void NetVis::sighandler(int)
389 {
390         exit(0);
391 }
392
393 Application::RegApp<NetVis> NetVis::reg;