ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/wvsniff/wvsniff
Revision: 1.12
Committed: Sat Jul 18 05:59:59 2009 UTC (14 years, 9 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +17 -9 lines
Log Message:
riddify us of meta.yml garbage in manifest

File Contents

# Content
1 #!/usr/bin/perl
2
3 use Event;
4 use Time::HiRes qw(time);
5 use Curses;
6 use Socket;
7 use Storable;
8
9 $DEV = "eth1";
10 $RAWDEV = "wifi0";
11 $PROC = "/proc/driver/aironet/$DEV";
12 $STOREFILE = "wvsniff.dat";
13
14 my $GPS = eval { require GPSx; new GPS };
15
16 $SIG{INT} =
17 $SIG{TERM} =
18 $SIG{HUP} = sub { exit };
19
20 sub config {
21 open my $config, ">$PROC/Config"
22 or die "$PROC/Config: $!";
23 print $config $_[0];
24 }
25
26 config "Mode: yna\n";
27 system "ifconfig", $DEV, "up";
28 system "ifconfig", $RAWDEV, "up";
29
30 my $initscr;
31 sub init_screen {
32 initscr;
33 $initscr = 1;
34 start_color;
35 cbreak; noecho;
36 immedok 0; nonl; raw; intrflush 0; keypad 1;
37 idlok 1; scrollok 0; leaveok 0;
38 }
39
40 sub end_win {
41 endwin if $initscr--;
42 }
43
44 init_screen;
45
46 sub signal_quality {
47 open my $stats, "<", "$PROC/Status"
48 or return 0;
49 sysread $stats, $buf, 1024;
50 $buf =~ /Signal Quality: (\d+)/ && $1;
51 }
52
53 my $refresh = Event->idle(nice => -5, parked => 1, min => 0.01, max => 1, repeat => 0, cb => sub {
54 @menu = ();
55 &$curmenu;
56
57 $menu_idx += @menu if $menu_idx < 0;
58 $menu_idx -= @menu if $menu_idx >= @menu;
59
60 erase;
61
62 my $data = $GPS ? $GPS->data : {};
63
64 move 0, 0;
65 addstr $frame++ . ": " . signal_quality . " " . localtime($data->{time}) . " $data->{lat} $data->{long} $data->{alt}m";
66
67 for my $idx (0 .. $#menu) {
68 move 2 + $idx, 0;
69 standout if $idx == $menu_idx;
70 addstr $menu[$idx][0];
71 standend if $idx == $menu_idx;
72 }
73
74 move 3 + scalar@menu, 0;
75 eval {
76 $menu[$menu_idx][1]->();
77 };
78
79 refresh;
80 });
81
82 Event->timer(after => 0, interval => 2, cb => sub { $refresh->start });
83
84 Event->io(fd => \*STDIN, poll => 'r', cb => sub {
85 my $ch = getch;
86 if ($ch eq "\x1b" or $ch eq "q") {
87 Event::unloop;
88 } elsif ($ch eq KEY_DOWN or $ch eq "j") {
89 $menu_idx++;
90 } elsif ($ch eq KEY_UP or $ch eq "k") {
91 $menu_idx--;
92 } else {
93 eval {
94 $menu[$menu_idx][2]->($ch);
95 };
96 }
97 $refresh->start;
98 });
99
100 -r $STOREFILE and
101 $db = Storable::retrieve $STOREFILE;
102
103 END {
104 $db->{version} = 0;
105 Storable::store $db, $STOREFILE;
106 end_win;
107 config "Mode: adhoc\n";
108 }
109
110 sub decode_tags {
111 my $pkt = shift;
112 my %tag;
113
114 while ($pkt) {
115 my ($id, $len) = unpack "C C", $pkt;
116 my $data = substr $pkt, 2, $len;
117 $pkt = substr $pkt, 2 + $len;
118
119 $id = ({
120 0 => SSID,
121 1 => rates,
122 2 => FH,
123 3 => DS,
124 4 => CF,
125 5 => TIM,
126 6 => IBSS,
127 16 => challenge,
128 })->{$id};
129 $tag{$id} = $data;
130 }
131
132 %tag;
133 }
134
135 sub PF_PACKET (){ 17 }
136 sub SOCK_RAW (){ 3 }
137 sub ETH_P_ALL (){ 0x0003 }
138 sub SIOCGIFINDEX (){ 0x000008933 }
139
140 sub open_pcap_socket {
141 socket my $cap, PF_PACKET, SOCK_RAW, (unpack "s", pack "n", ETH_P_ALL)
142 or die "unable to open packet socket: $!";
143
144 my $ifidx = pack "a16 I", $RAWDEV;
145 ioctl $cap, SIOCGIFINDEX, $ifidx
146 or die "can't get index of interface $RAWDEV: $!";
147 $ifidx = unpack "x16 I", $ifidx;
148
149 $::sockaddr = pack "S! S! I S! C C a8", PF_PACKET, (unpack "s", pack "n", ETH_P_ALL), $ifidx;
150 bind $cap, $::sockaddr
151 or die "unable to bind to interface: $!";
152
153 $cap;
154 }
155
156 sub escape($) {
157 local $_ = $_[0];
158 s/([^\x20-\x7e])/sprintf "\\%03o", ord $1/ge;
159 $_;
160 }
161
162 sub e2h($) {
163 join ":", map unpack("H*", $_), split //, $_[0];
164 }
165
166 sub add_menu {
167 my ($title, $display, $select) = @_;
168 push @menu, [$title, $display, $select];
169 }
170
171 $curmenu = \&menu_bss_list;
172
173 sub bss2string {
174 my $bss = shift;
175
176 my $s = "AGE(" . int(time - $bss->{ts}) . ") "
177 . "IP($bss->{ip}) "
178 . "ARP($bss->{arp}) ";
179 while (my ($k, $v) = each %{$bss->{ap}}) {
180 $s .= "(" . e2h($k) . " $v->{mode} '".escape($v->{essid})."') ";
181 }
182 $s;
183 }
184
185 sub show_map {
186 my ($map, $w, $h) = @_;
187
188 my $data = $GPS->data;
189 local $map->{"$data->{lat};$data->{long}"} = 0;
190
191 my @data = (("-" x $w) x $h);
192
193 my ($x1, $y1, $x2, $y2) = (1e6, 1e6, 1e-6, 1e-6);
194 my $level = 1;
195
196 while (my ($k, $v) = each %$map) {
197 my ($y, $x) = split /;/, $k;
198 $x1 = $x if $x1 > $x;
199 $x2 = $x if $x2 < $x;
200 $y1 = $y if $y1 > $y;
201 $y2 = $y if $y2 < $y;
202 $level = $v if $level < $v;
203 }
204
205 $x2 = ($w - 1) / (($x2 - $x1) || 1e-6);
206 $y2 = ($h - 1) / (($y2 - $y1) || 1e-6);
207
208 while (my ($k, $v) = each %$map) {
209 my ($y, $x) = split /;/, $k;
210
211 $y = ($y - $y1) * $y2;
212 $x = ($x - $x1) * $x2;
213
214 substr $data[$h-1-int $y], $x, 1, int($map->{$k} * 9 / $level);
215 }
216
217 for (@data) {
218 addstr $_ . "\n";
219 }
220 }
221
222 sub display_bss {
223 my $bss = shift;
224
225 addstr "TS: $bss->{ts}\n";
226 addstr "WEP/Weak packets received: $bss->{wep}/$bss->{weak}\n";
227 addstr "ARP/IP packets received: $bss->{arp}/$bss->{ip}\n";
228
229 addstr "\naccess points\n";
230
231 while (my ($k, $v) = each %{$bss->{ap}}) {
232 addstr " " . e2h($k) . "\n";
233 addstr " mode $v->{mode}; channel $v->{channel}; essid '".escape($v->{essid})."'; beacon frames $v->{beacon}\n";
234 }
235
236 addstr "\nstations\n";
237
238 while (my ($k, $v) = each %{$bss->{station}}) {
239 addstr " " . e2h($k);
240 while (my ($k, $v) = each %$v) {
241 addstr " " . (inet_ntoa $k) . "($v)";
242 }
243 addstr "\n";
244 }
245
246 if ($bss->{gps}) {
247 my ($x, $y); getyx $y, $x;
248 addstr "\nmap\n";
249 my $h = (&getmaxy - $y) - 3;
250 show_map $bss->{gps}, int($h*1.8), $h;
251 }
252 }
253
254 sub menu_bss_list {
255 my @menu;
256 #for my $k (sort { int $db->{$b}{ts} <=> int $db->{$a}{ts} } keys %$db) {
257 for my $k (keys %$db) {
258 my $v = $db->{$k};
259 add_menu e2h($k) . " " . bss2string($v),
260 sub { display_bss $v },
261 sub {
262 if ($_[0] eq KEY_LEFT or $_[0] eq "h") {
263 Event::unloop;
264 } elsif ($_[0] eq KEY_RIGHT or $_[0] eq "l") {
265 #$curmenu = sub { display_bss $k };
266 }
267 };
268 }
269 }
270
271 sub activity {
272 if ($GPS) {
273 my $data = $GPS->data;
274 $db->{$_[0]}{gps}{"$data->{lat};$data->{long}"} = signal_quality;
275 }
276 $db->{$_[0]}->{ts} = time;
277 $refresh->start;
278 }
279
280 sub reg_ip {
281 my ($bssid, $ip, $ether) = @_;
282 $db->{$bssid}{station}{$ether}{$ip}++;
283 activity $bssid;
284 }
285
286 {
287 my $cap = open_pcap_socket;
288 my $pkt;
289 open LOG, ">/tmp/log";#d#
290
291 Event->io(fd => $cap, poll => 'r', cb => sub {
292 sysread $cap, $pkt, 120;
293
294 printf LOG "%s\n", unpack "H*", $pkt;
295
296 my ($fc1, $fc2, $sid, $a1, $a2, $a3, $sc, $pkt)
297 = unpack "C C n a6 a6 a6 S a*", $pkt;
298
299 send $cap, (pack "C C n a6 a6 a6 S a*", $fc1, $fc2, $sid, $a2, $a1, $a3, $sc, $pkt), 0, $::sockaddr;
300
301 my $a4;
302
303 if ($fc2 & 0x03 == 0x03) {
304 $a4 = substr $pkt, 0, 6;
305 $pkt = substr $pkt, 6;
306 }
307
308 if ($fc1 == 0x80 or $fc1 == 0x50) {
309 my ($ts1, $ts2, $bi, $cf, $pkt)
310 = unpack "L L S S a*", $pkt;
311
312 my %tag = decode_tags $pkt;
313
314 my $ap = $db->{$a3}{ap}{$a2} ||= {};
315 $ap->{mode} = ($cf & 3) == 1 ? "AP" : "adhoc";
316
317 # ESSID ' ' workaround for "closed-network feature"
318 $ap->{essid} = $tag{SSID} unless $ap->{essid} && $tag{SSID} eq " ";
319 $ap->{channel} = ord $tag{DS};
320 $ap->{beacon}++;
321
322 activity $a3;
323 } elsif ($fc1 == 0x08) {
324 my $bssid = ($a3, $a1, $a2, $a2)[$fc2 & 3];
325
326 if ($fc2 & 0x40) {
327 my ($iv1, $iv2, $iv3, $ivopt, $byte) = unpack "C C C C C", $pkt;
328 $db->{$bssid}{wep}++;
329 if ($iv1 > 2 and $iv1 < 14 and $iv2 == 255) {
330 $db->{$bssid}{weak}++;
331 }
332 } else {
333 my ($llc, $et, $pkt) = unpack "a6 n a*", $pkt;
334 if ($llc eq "\xaa\xaa\x03\x00\x00\x00"
335 or $llc eq "\xaa\xaa\x03\x00\x00\xf8") { # SNAP/UI/{ENCAP_ETHER,CISCO}
336 if ($et == 0x0800) {
337 my ($vhl, $tos, $len, $id, $off, $ttl, $prot, $sum, $src, $dst, $pkt)
338 = unpack "C C n n n C C n a4 a4 a*", $pkt;
339 if ($vhl & 0x40 == 0x40) {
340 $db->{$bssid}{ip}++;
341 reg_ip $bssid, $src, ($a2, $a2, $a3, $a4)[$fc2 & 3];
342 reg_ip $bssid, $dst, ($a1, $a3, $a1, $a3)[$fc2 & 3];
343 }
344 } elsif ($et == 0x0806) {
345 my ($hrd, $pro, $hln, $pln, $op, $src_hw, $src, $dst_hw, $dst)
346 = unpack "n n C C n a6 a4 a6 a4", $pkt;
347 if ($hrd == 1 and $hln == 6 and $pln == 4) {
348 reg_ip $bssid, $src, $src_hw;
349 reg_ip $bssid, $dst, $dst_hw if $op != 1;
350 $db->{$bssid}{arp}++;
351 }
352 }
353 } else {
354 0 &&
355 printf "?? %02x %02x %04x %04x: %s.%s \n", $fc1, $fc2, $sid, $sc, (unpack "H*", $llc), unpack "H*", $pkt;
356 }
357 }
358 } elsif ($fc1 != 0x40 and $fc1 != 0x10 and $fc1 != 0x00 and $fc1 != 0xb0) {
359 0 &&
360 printf "%02x %02x %04x %04x: %s \n", $fc1, $fc2, $sid, $sc, unpack "H*", $pkt;
361 }
362 });
363 }
364
365 Event::loop;
366
367