1 |
root |
1.1 |
#!/usr/bin/perl |
2 |
|
|
|
3 |
|
|
use Event; |
4 |
|
|
use Time::HiRes qw(time); |
5 |
|
|
use Curses; |
6 |
|
|
use Socket; |
7 |
|
|
|
8 |
|
|
$DEV = "eth1"; |
9 |
|
|
$RAWDEV = "wifi0"; |
10 |
|
|
$PROC = "/proc/driver/aironet/$DEV"; |
11 |
|
|
|
12 |
|
|
$SIG{INT} = |
13 |
|
|
$SIG{TERM} = |
14 |
|
|
$SIG{HUP} = sub { exit }; |
15 |
|
|
|
16 |
|
|
sub config { |
17 |
|
|
open my $config, ">$PROC/Config" |
18 |
|
|
or die "$PROC/Config: $!"; |
19 |
|
|
print $config $_[0]; |
20 |
|
|
} |
21 |
|
|
|
22 |
|
|
config "Mode: yna\n"; |
23 |
|
|
system "ifconfig", $DEV, "up"; |
24 |
|
|
system "ifconfig", $RAWDEV, "up"; |
25 |
|
|
|
26 |
|
|
my $initscr; |
27 |
|
|
sub init_screen { |
28 |
|
|
initscr; |
29 |
|
|
$initscr = 1; |
30 |
|
|
start_color; |
31 |
|
|
cbreak; noecho; |
32 |
|
|
immedok 0; nonl; raw; intrflush 0; keypad 1; |
33 |
|
|
idlok 1; scrollok 0; leaveok 0; |
34 |
|
|
} |
35 |
|
|
|
36 |
|
|
sub end_win { |
37 |
|
|
endwin if $initscr--; |
38 |
|
|
} |
39 |
|
|
|
40 |
|
|
init_screen; |
41 |
|
|
|
42 |
|
|
my $refresh = Event->idle(nice => -5, parked => 1, min => 0.01, max => 1, repeat => 0, cb => sub { |
43 |
|
|
@menu = (); |
44 |
|
|
&$curmenu; |
45 |
|
|
|
46 |
|
|
$menu_idx += @menu if $menu_idx < 0; |
47 |
|
|
$menu_idx -= @menu if $menu_idx >= @menu; |
48 |
|
|
|
49 |
|
|
erase; |
50 |
|
|
|
51 |
|
|
move 0, 0; |
52 |
|
|
addstr $frame++; |
53 |
|
|
|
54 |
|
|
for my $idx (0 .. $#menu) { |
55 |
|
|
move 1 + $idx, 0; |
56 |
|
|
standout if $idx == $menu_idx; |
57 |
|
|
addstr $menu[$idx][0]; |
58 |
|
|
standend if $idx == $menu_idx; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
move 2 + scalar@menu, 0; |
62 |
|
|
eval { |
63 |
|
|
$menu[$menu_idx][1]->(); |
64 |
|
|
}; |
65 |
|
|
|
66 |
|
|
refresh; |
67 |
|
|
}); |
68 |
|
|
|
69 |
|
|
Event->io(fd => \*STDIN, poll => 'r', cb => sub { |
70 |
|
|
my $ch = getch; |
71 |
|
|
if ($ch eq "\x1b" or $ch eq "q") { |
72 |
|
|
Event::unloop; |
73 |
|
|
} elsif ($ch eq KEY_DOWN or $ch eq "j") { |
74 |
|
|
$menu_idx++; |
75 |
|
|
} elsif ($ch eq KEY_UP or $ch eq "k") { |
76 |
|
|
$menu_idx--; |
77 |
|
|
} else { |
78 |
|
|
eval { |
79 |
|
|
$menu[$menu_idx][2]->($ch); |
80 |
|
|
}; |
81 |
|
|
} |
82 |
|
|
$refresh->start; |
83 |
|
|
}); |
84 |
|
|
|
85 |
|
|
END { |
86 |
|
|
end_win; |
87 |
|
|
config "Mode: adhoc\n"; |
88 |
|
|
} |
89 |
|
|
|
90 |
|
|
sub decode_tags { |
91 |
|
|
my $pkt = shift; |
92 |
|
|
my %tag; |
93 |
|
|
|
94 |
|
|
while ($pkt) { |
95 |
|
|
my ($id, $len) = unpack "C C", $pkt; |
96 |
|
|
my $data = substr $pkt, 2, $len; |
97 |
|
|
$pkt = substr $pkt, 2 + $len; |
98 |
|
|
|
99 |
|
|
$id = ({ |
100 |
|
|
0 => SSID, |
101 |
|
|
1 => rates, |
102 |
|
|
2 => FH, |
103 |
|
|
3 => DS, |
104 |
|
|
4 => CF, |
105 |
|
|
5 => TIM, |
106 |
|
|
6 => IBSS, |
107 |
|
|
16 => challenge, |
108 |
|
|
})->{$id}; |
109 |
|
|
$tag{$id} = $data; |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
%tag; |
113 |
|
|
} |
114 |
|
|
|
115 |
|
|
sub PF_PACKET (){ 17 } |
116 |
|
|
sub SOCK_RAW (){ 3 } |
117 |
|
|
sub ETH_P_ALL (){ 0x0003 } |
118 |
|
|
sub SIOCGIFINDEX (){ 0x000008933 } |
119 |
|
|
|
120 |
|
|
sub open_pcap_socket { |
121 |
|
|
socket my $cap, PF_PACKET, SOCK_RAW, (unpack "s", pack "n", ETH_P_ALL) |
122 |
|
|
or die "unable to open packet socket: $!"; |
123 |
|
|
|
124 |
|
|
my $ifidx = pack "a16 I", $RAWDEV; |
125 |
|
|
ioctl $cap, SIOCGIFINDEX, $ifidx |
126 |
|
|
or die "can't get index of interface $RAWDEV: $!"; |
127 |
|
|
$ifidx = unpack "x16 I", $ifidx; |
128 |
|
|
|
129 |
|
|
bind $cap, pack "S! S! I S! C C a8", PF_PACKET, (unpack "s", pack "n", ETH_P_ALL), $ifidx |
130 |
|
|
or die "unable to bind to interface: $!"; |
131 |
|
|
|
132 |
|
|
$cap; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
sub e2h($) { |
136 |
|
|
join ":", map unpack("H*", $_), split //, $_[0]; |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
sub add_menu { |
140 |
|
|
my ($title, $display, $select) = @_; |
141 |
|
|
push @menu, [$title, $display, $select]; |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
$curmenu = \&menu_bss_list; |
145 |
|
|
|
146 |
|
|
sub bss2string { |
147 |
|
|
my $bss = shift; |
148 |
|
|
|
149 |
|
|
my $s = "AGE(" . int(time - $bss->{ts}) . ") " |
150 |
|
|
. "IP($bss->{ip}) " |
151 |
|
|
. "ARP($bss->{arp}) "; |
152 |
|
|
while (my ($k, $v) = each %{$bss->{ap}}) { |
153 |
|
|
$s .= "(" . e2h($k) . " $v->{mode} '$v->{essid}') "; |
154 |
|
|
} |
155 |
|
|
$s; |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
sub display_bss { |
159 |
|
|
my $bss = shift; |
160 |
|
|
|
161 |
|
|
addstr "TS: $bss->{ts}\n"; |
162 |
|
|
addstr "ARP/IP packets received: $bss->{arp}/$bss->{ip}\n"; |
163 |
|
|
|
164 |
|
|
addstr "\naccess points\n"; |
165 |
|
|
|
166 |
|
|
while (my ($k, $v) = each %{$bss->{ap}}) { |
167 |
|
|
addstr " " . e2h($k) . "\n"; |
168 |
|
|
addstr " mode $v->{mode}, channel $v->{channel}, essid '$v->{essid}'\n"; |
169 |
|
|
addstr " beacon frames received $v->{beacon}\n"; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
addstr "\nstations\n"; |
173 |
|
|
|
174 |
|
|
while (my ($k, $v) = each %{$bss->{station}}) { |
175 |
|
|
addstr " " . e2h($k); |
176 |
|
|
while (my ($k, $v) = each %$v) { |
177 |
|
|
addstr " " . (inet_ntoa $k) . "($v)"; |
178 |
|
|
} |
179 |
|
|
addstr "\n"; |
180 |
|
|
} |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
sub menu_bss_list { |
184 |
|
|
my @menu; |
185 |
|
|
while (my ($k, $v) = each %$db) { |
186 |
|
|
add_menu e2h($k) . " " . bss2string($v), |
187 |
|
|
sub { display_bss $v }, |
188 |
|
|
sub { |
189 |
|
|
if ($_[0] eq KEY_LEFT or $_[0] eq "h") { |
190 |
|
|
Event::unloop; |
191 |
|
|
} elsif ($_[0] eq KEY_RIGHT or $_[0] eq "l") { |
192 |
|
|
#$curmenu = sub { display_bss $k }; |
193 |
|
|
} |
194 |
|
|
} for 1..3; |
195 |
|
|
} |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
sub activity { |
199 |
|
|
$db->{$_[0]}->{ts} = time; |
200 |
|
|
#print "activity ", e2h $_[0]; print "\n"; |
201 |
|
|
$refresh->start; |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
sub reg_ip { |
205 |
|
|
my ($bssid, $ip, $ether) = @_; |
206 |
|
|
$db->{$bssid}{station}{$ether}{$ip}++; |
207 |
|
|
activity $bssid; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
{ |
211 |
|
|
my $cap = open_pcap_socket; |
212 |
|
|
my $pkt; |
213 |
|
|
|
214 |
|
|
Event->io(fd => $cap, poll => 'r', cb => sub { |
215 |
|
|
sysread $$cap, $pkt, 120; |
216 |
|
|
|
217 |
|
|
my ($fc1, $fc2, $sid, $a1, $a2, $a3, $sc, $pkt) |
218 |
|
|
= unpack "C C n a6 a6 a6 S a*", $pkt; |
219 |
|
|
|
220 |
|
|
$pkt = substr $pkt, 6 if $fc2 & 0x03 == 0x03; # skip A4 |
221 |
|
|
|
222 |
|
|
if ($fc1 == 0x80 or $fc1 == 0x50) { |
223 |
|
|
my ($ts1, $ts2, $bi, $cf, $pkt) |
224 |
|
|
= unpack "L L S S a*", $pkt; |
225 |
|
|
|
226 |
|
|
my %tag = decode_tags $pkt; |
227 |
|
|
|
228 |
|
|
my $ap = $db->{$a3}{ap}{$a2} ||= {}; |
229 |
|
|
$ap->{mode} = ($cf & 3) == 1 ? "AP" : "adhoc"; |
230 |
|
|
$ap->{essid} = $tag{SSID}; |
231 |
|
|
$ap->{channel} = ord $tag{DS}; |
232 |
|
|
$ap->{beacon}++; |
233 |
|
|
|
234 |
|
|
activity $a3; |
235 |
|
|
} elsif ($fc1 == 0x08) { |
236 |
|
|
my $bssid = ($a3, $a2, $a1)[$fc2 & 3]; |
237 |
|
|
|
238 |
|
|
my ($llc, $et, $pkt) = unpack "a6 n a*", $pkt; |
239 |
|
|
if ($llc eq "\xaa\xaa\x03\x00\x00\x00") { # SNAP/UI/ENCAP_ETHER |
240 |
|
|
if ($et == 0x0800) { |
241 |
|
|
my ($vhl, $tos, $len, $id, $off, $ttl, $prot, $sum, $src, $dst, $pkt) |
242 |
|
|
= unpack "C C n n n C C n a4 a4 a*", $pkt; |
243 |
|
|
if ($vhl & 0x40 == 0x40) { |
244 |
|
|
$db->{$bssid}{ip}++; |
245 |
|
|
reg_ip $bssid, $src, $a2; |
246 |
|
|
reg_ip $bssid, $dst, $a1; |
247 |
|
|
0 && |
248 |
|
|
printf "IP: %02x %02x %04x %04x: %s %s> %s %s %02x \n", |
249 |
|
|
$fc1, $fc2, $sid, $sc, |
250 |
|
|
(unpack "H*", $a2), (inet_ntoa $src), |
251 |
|
|
(unpack "H*", $a1), (inet_ntoa $dst), |
252 |
|
|
$prot; |
253 |
|
|
} |
254 |
|
|
} elsif ($et == 0x0806) { |
255 |
|
|
my ($hrd, $pro, $hln, $pln, $op, $src_hw, $src, $dst_hw, $dst) |
256 |
|
|
= unpack "n n C C n a6 a4 a6 a4", $pkt; |
257 |
|
|
if ($hrd == 1 and $hln == 6 and $pln == 4) { |
258 |
|
|
reg_ip $bssid, $src, $src_hw; |
259 |
|
|
reg_ip $bssid, $dst, $dst_hw if $op != 1; |
260 |
|
|
$db->{$bssid}{arp}++; |
261 |
|
|
0 && |
262 |
|
|
printf "ARP: %04x> %s %s : %s %s\n", |
263 |
|
|
$op, |
264 |
|
|
(unpack "H*", $src_hw), (inet_ntoa $src), |
265 |
|
|
(unpack "H*", $dst_hw), (inet_ntoa $dst); |
266 |
|
|
} |
267 |
|
|
} |
268 |
|
|
} else { |
269 |
|
|
0 && |
270 |
|
|
printf "?? %02x %02x %04x %04x: %s.%s \n", $fc1, $fc2, $sid, $sc, (unpack "H*", $llc), unpack "H*", $pkt; |
271 |
|
|
} |
272 |
|
|
} elsif ($fc1 != 0x40 and $fc1 != 0x10 and $fc1 != 0x00 and $fc1 != 0xb0) { |
273 |
|
|
0 && |
274 |
|
|
printf "%02x %02x %04x %04x: %s \n", $fc1, $fc2, $sid, $sc, unpack "H*", $pkt; |
275 |
|
|
} |
276 |
|
|
}); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
Event::loop; |
280 |
|
|
|
281 |
|
|
|