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, 10 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

# User Rev Content
1 root 1.1 #!/usr/bin/perl
2    
3     use Event;
4     use Time::HiRes qw(time);
5     use Curses;
6     use Socket;
7 root 1.7 use Storable;
8 root 1.1
9 root 1.7 $DEV = "eth1";
10     $RAWDEV = "wifi0";
11     $PROC = "/proc/driver/aironet/$DEV";
12 root 1.9 $STOREFILE = "wvsniff.dat";
13 root 1.1
14 root 1.12 my $GPS = eval { require GPSx; new GPS };
15    
16 root 1.1 $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 root 1.6 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 root 1.1 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 root 1.5 my $data = $GPS ? $GPS->data : {};
63 root 1.2
64 root 1.1 move 0, 0;
65 root 1.6 addstr $frame++ . ": " . signal_quality . " " . localtime($data->{time}) . " $data->{lat} $data->{long} $data->{alt}m";
66 root 1.1
67     for my $idx (0 .. $#menu) {
68 root 1.3 move 2 + $idx, 0;
69 root 1.1 standout if $idx == $menu_idx;
70     addstr $menu[$idx][0];
71     standend if $idx == $menu_idx;
72     }
73    
74 root 1.3 move 3 + scalar@menu, 0;
75 root 1.1 eval {
76     $menu[$menu_idx][1]->();
77     };
78    
79     refresh;
80     });
81    
82 root 1.8 Event->timer(after => 0, interval => 2, cb => sub { $refresh->start });
83    
84 root 1.1 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 root 1.7 -r $STOREFILE and
101     $db = Storable::retrieve $STOREFILE;
102    
103 root 1.1 END {
104 root 1.8 $db->{version} = 0;
105 root 1.7 Storable::store $db, $STOREFILE;
106 root 1.1 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 root 1.12 $::sockaddr = pack "S! S! I S! C C a8", PF_PACKET, (unpack "s", pack "n", ETH_P_ALL), $ifidx;
150     bind $cap, $::sockaddr
151 root 1.1 or die "unable to bind to interface: $!";
152    
153     $cap;
154     }
155    
156 root 1.12 sub escape($) {
157     local $_ = $_[0];
158     s/([^\x20-\x7e])/sprintf "\\%03o", ord $1/ge;
159     $_;
160     }
161    
162 root 1.1 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 root 1.12 $s .= "(" . e2h($k) . " $v->{mode} '".escape($v->{essid})."') ";
181 root 1.1 }
182     $s;
183     }
184    
185 root 1.5 sub show_map {
186     my ($map, $w, $h) = @_;
187    
188 root 1.6 my $data = $GPS->data;
189     local $map->{"$data->{lat};$data->{long}"} = 0;
190    
191 root 1.5 my @data = (("-" x $w) x $h);
192    
193     my ($x1, $y1, $x2, $y2) = (1e6, 1e6, 1e-6, 1e-6);
194 root 1.6 my $level = 1;
195 root 1.5
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 root 1.6 $level = $v if $level < $v;
203 root 1.5 }
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 root 1.12 substr $data[$h-1-int $y], $x, 1, int($map->{$k} * 9 / $level);
215 root 1.5 }
216    
217     for (@data) {
218     addstr $_ . "\n";
219     }
220     }
221    
222 root 1.1 sub display_bss {
223     my $bss = shift;
224    
225     addstr "TS: $bss->{ts}\n";
226 root 1.6 addstr "WEP/Weak packets received: $bss->{wep}/$bss->{weak}\n";
227     addstr "ARP/IP packets received: $bss->{arp}/$bss->{ip}\n";
228 root 1.1
229     addstr "\naccess points\n";
230    
231     while (my ($k, $v) = each %{$bss->{ap}}) {
232     addstr " " . e2h($k) . "\n";
233 root 1.12 addstr " mode $v->{mode}; channel $v->{channel}; essid '".escape($v->{essid})."'; beacon frames $v->{beacon}\n";
234 root 1.1 }
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 root 1.5
246     if ($bss->{gps}) {
247 root 1.6 my ($x, $y); getyx $y, $x;
248 root 1.5 addstr "\nmap\n";
249 root 1.6 my $h = (&getmaxy - $y) - 3;
250     show_map $bss->{gps}, int($h*1.8), $h;
251 root 1.5 }
252 root 1.1 }
253    
254     sub menu_bss_list {
255     my @menu;
256 root 1.12 #for my $k (sort { int $db->{$b}{ts} <=> int $db->{$a}{ts} } keys %$db) {
257     for my $k (keys %$db) {
258 root 1.8 my $v = $db->{$k};
259 root 1.1 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 root 1.4 };
268 root 1.1 }
269     }
270    
271     sub activity {
272 root 1.5 if ($GPS) {
273     my $data = $GPS->data;
274 root 1.6 $db->{$_[0]}{gps}{"$data->{lat};$data->{long}"} = signal_quality;
275 root 1.5 }
276 root 1.1 $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 root 1.8 open LOG, ">/tmp/log";#d#
290 root 1.1
291     Event->io(fd => $cap, poll => 'r', cb => sub {
292 root 1.11 sysread $cap, $pkt, 120;
293 root 1.1
294 root 1.8 printf LOG "%s\n", unpack "H*", $pkt;
295    
296 root 1.1 my ($fc1, $fc2, $sid, $a1, $a2, $a3, $sc, $pkt)
297     = unpack "C C n a6 a6 a6 S a*", $pkt;
298    
299 root 1.12 send $cap, (pack "C C n a6 a6 a6 S a*", $fc1, $fc2, $sid, $a2, $a1, $a3, $sc, $pkt), 0, $::sockaddr;
300    
301 root 1.8 my $a4;
302    
303     if ($fc2 & 0x03 == 0x03) {
304     $a4 = substr $pkt, 0, 6;
305     $pkt = substr $pkt, 6;
306     }
307 root 1.1
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 root 1.8
317 root 1.10 # ESSID ' ' workaround for "closed-network feature"
318 root 1.8 $ap->{essid} = $tag{SSID} unless $ap->{essid} && $tag{SSID} eq " ";
319 root 1.1 $ap->{channel} = ord $tag{DS};
320     $ap->{beacon}++;
321    
322     activity $a3;
323     } elsif ($fc1 == 0x08) {
324 root 1.8 my $bssid = ($a3, $a1, $a2, $a2)[$fc2 & 3];
325 root 1.1
326 root 1.5 if ($fc2 & 0x40) {
327 root 1.6 my ($iv1, $iv2, $iv3, $ivopt, $byte) = unpack "C C C C C", $pkt;
328 root 1.5 $db->{$bssid}{wep}++;
329 root 1.6 if ($iv1 > 2 and $iv1 < 14 and $iv2 == 255) {
330     $db->{$bssid}{weak}++;
331     }
332 root 1.5 } else {
333     my ($llc, $et, $pkt) = unpack "a6 n a*", $pkt;
334 root 1.8 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 root 1.5 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 root 1.8 reg_ip $bssid, $src, ($a2, $a2, $a3, $a4)[$fc2 & 3];
342     reg_ip $bssid, $dst, ($a1, $a3, $a1, $a3)[$fc2 & 3];
343 root 1.5 }
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 root 1.1 }
353 root 1.5 } else {
354     0 &&
355     printf "?? %02x %02x %04x %04x: %s.%s \n", $fc1, $fc2, $sid, $sc, (unpack "H*", $llc), unpack "H*", $pkt;
356 root 1.1 }
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