wifi-diag: Initial framework for script to diagnose wifi pcaps

End goal is to be able to find block-ack issues and provide useful
graphs to help debug complex wifi networks.
This commit is contained in:
Ben Greear
2018-10-04 13:14:52 -07:00
parent 559580b9af
commit 732a7d8aea
4 changed files with 361 additions and 0 deletions

150
wifi_diag/Packet.pm Normal file
View File

@@ -0,0 +1,150 @@
package Packet;
use warnings;
use strict;
sub new {
my $class = shift;
my %options = @_;
my $self = {
type_subtype => "UNKNOWN",
receiver => "UNKNOWN",
transmitter => "UNKNOWN",
%options,
amsdu_frame_count => 0,
ssi_sig_found => 0,
tid => 17, # anything that does not specify a tid gets this.
};
bless($self, $class);
return($self);
}
sub raw_pkt {
my $self = shift;
return $self->{raw_pkt};
}
sub append {
my $self = shift;
my $ln = shift;
$self->{raw_pkt} .= $ln;
if ($ln =~ /\s*Transmitter address: .*\((\S+)\)/) {
$self->{transmitter} = $1;
}
elsif ($ln =~ /\s*Receiver address: .*\((\S+)\)/) {
$self->{receiver} = $1;
}
elsif ($ln =~ /\s*Fragment number: (\d+)/) {
$self->{fragno} = $1;
}
elsif ($ln =~ /\s*Sequence number: (\d+)/) {
$self->{seqno} = $1;
}
elsif ($ln =~ /\s*Type\/Subtype: (.*)/) {
$self->{type_subtype} = $1;
}
elsif ($ln =~ /.* = Starting Sequence Number: (\d+)/) {
$self->{ba_starting_seq} = $1;
}
elsif ($ln =~ /.* = TID for which a Basic BlockAck frame is requested: (\S+)/) {
$self->{ba_tid} = $1;
}
elsif ($ln =~ /.*Block Ack Bitmap: (\S+)/) {
$self->{ba_bitmap} = $1;
}
elsif ($ln =~ /\s*Data Rate: (.*)/) {
$self->{datarate} = $1;
}
elsif ($ln =~ /\s*VHT information/) {
$self->{is_vht} = 1;
}
elsif ($ln =~ /\s*Bandwidth: (.*)/) {
$self->{bandwidth} = $1;
}
elsif ($ln =~ /\s*User 0: MCS (.*)/) {
$self->{mcs} = $1;
}
elsif ($ln =~ /.* = Spatial streams 0: (.*)/) {
$self->{nss} = $1;
}
elsif ($ln =~ /.* = TID: (.*)/) {
$self->{tid} = $1;
}
elsif ($ln =~ /.* = Payload Type: (.*)/) {
$self->{payload_type} = $1;
}
elsif ($ln =~ /\s+\[Data Rate: (.*)\]/) {
$self->{datarate} = $1;
}
elsif ($ln =~ /\s*SSI Signal: (.*)/) {
if ($self->{ssi_sig_found} == 0) {
$self->{ssi_combined} = $1;
$self->{ssi_sig_found}++;
}
elsif ($self->{ssi_sig_found} == 1) {
$self->{ssi_ant_0} = $1;
$self->{ssi_sig_found}++;
}
elsif ($self->{ssi_sig_found} == 2) {
$self->{ssi_ant_1} = $1;
$self->{ssi_sig_found}++;
}
elsif ($self->{ssi_sig_found} == 3) {
$self->{ssi_ant_2} = $1;
$self->{ssi_sig_found}++;
}
elsif ($self->{ssi_sig_found} == 4) {
$self->{ssi_ant_3} = $1;
$self->{ssi_sig_found}++;
}
}
# AMPDU and such...
elsif ($ln =~ /\s*A-MSDU Subframe #(\d+)/) {
if ($1 > $self->{amsdu_frame_count}) {
$self->{amsdu_frame_count} = $1;
}
}
}
sub frame_num {
my $self = shift;
return $self->{frame_num};
}
sub type_subtype {
my $self = shift;
return $self->{type_subtype};
}
sub transmitter {
my $self = shift;
return $self->{transmitter};
}
sub receiver {
my $self = shift;
return $self->{receiver};
}
sub tid {
my $self = shift;
return $self->{tid};
}
sub set_transmitter {
my $self = shift;
my $tx = shift;
$self->{transmitter} = $tx;
}
sub set_receiver {
my $self = shift;
my $rx = shift;
$self->{receiver} = $rx;
}
1;

80
wifi_diag/PeerConn.pm Normal file
View File

@@ -0,0 +1,80 @@
package PeerConn;
use warnings;
use strict;
use Tid;
sub new {
my $class = shift;
my %options = @_;
my $self = {
%options,
tids => [],
};
bless($self, $class);
return($self);
}
sub hash_str {
my $self = shift;
return $self->{local_addr} . "." . $self->{peer_addr};
}
sub local_addr {
my $self = shift;
return $self->{local_addr};
}
sub peer_addr {
my $self = shift;
return $self->{peer_addr};
}
sub add_pkt {
my $self = shift;
my $pkt = shift;
my $tidno = $pkt->tid();
my $tid = $self->find_or_create_tid($tidno);
$tid->add_pkt($pkt);
}
sub find_or_create_tid {
my $self = shift;
my $tidno = shift;
my $tid;
if (exists $self->{tids}[$tidno]) {
$tid = $self->{tids}[$tidno];
}
else {
$tid = Tid->new(tidno => $tidno);
$self->{tids}[$tidno] = $tid;
}
return $tid;
}
sub printme {
my $self = shift;
my $tid_count = @{$self->{tids}};
print "hash-key: " . $self->hash_str() . " tid-count: " . $tid_count . "\n";
my $i;
for ($i = 0; $i < $tid_count; $i++) {
#print "Checking tid: $i\n";
if (exists $self->{tids}[$i]) {
#print "Printing tid: $i\n";
$self->{tids}[$i]->printme();
#print "Done printing tid: $i\n";
}
}
#print "Done peer-conn printme\n";
return;
}
1;

42
wifi_diag/Tid.pm Normal file
View File

@@ -0,0 +1,42 @@
package Tid;
use warnings;
use strict;
sub new {
my $class = shift;
my %options = @_;
my $self = {
pkts => [],
%options,
};
bless($self, $class);
return($self);
}
sub tidno {
my $self = shift;
return $self->{tidno};
}
sub add_pkt {
my $self = shift;
my $pkt = shift;
push(@{$self->{pkts}}, $pkt);
}
sub get_pkts {
my $self = shift;
return @{$self->{pkts}};
}
sub printme {
my $self = shift;
print " tidno: " . $self->tidno() . "\n";
print " pkt-count: " . $self->get_pkts() . "\n";
}
1;

89
wifi_diag/wifi_pcap_diag.pl Executable file
View File

@@ -0,0 +1,89 @@
#!/usr/bin/perl -w
# Read in a decoded pcap file and generate report
# Usage: tshark -V -r wifi.pcap | ./wifi_pcap_diag.pl
use strict;
use warnings;
use diagnostics;
use Carp;
use PeerConn;
use Packet;
my %peer_conns = ();
my $cur_pkt = Packet->new(raw_pkt => "");
my $last_pkt = Packet->new(raw_pkt => "");
while (<>) {
my $ln = $_;
if ($ln =~ /^Frame (\d+):/) {
if ($cur_pkt->raw_pkt() ne "") {
processPkt($cur_pkt);
}
$cur_pkt = Packet->new(frame_num => $1,
raw_pkt => $ln);
}
else {
$cur_pkt->append($ln);
}
}
if ($cur_pkt->raw_pkt() ne "") {
processPkt($cur_pkt);
}
# Print out all peer-conns we found
for my $conn (values %peer_conns) {
$conn->printme();
}
exit 0;
sub processPkt {
my $pkt = shift;
# Find which station (or AP) sent this pkt.
# Add graph point for mcs vs time
# Add graph point for retransmits
# Check sequence-no gap
# If pkt is an ACK, it will not have a sender address. Guess based on
# previous packet.
if ($pkt->type_subtype() eq "Acknowledgement (0x001d)") {
if ($last_pkt->transmitter() eq $pkt->receiver()) {
$pkt->set_transmitter($last_pkt->receiver());
}
else {
print "ERROR: Frame " . $pkt->frame_num() . " is ACK for unknown packet.\n";
$last_pkt = $pkt;
return;
}
}
my $hash = $pkt->receiver() . "." . $pkt->transmitter();
my $hash2 = $pkt->transmitter() . "." . $pkt->receiver();
my $peer_conn;
if (exists $peer_conns{$hash}) {
$peer_conn = $peer_conns{$hash};
}
else {
if (exists $peer_conns{$hash2}) {
$peer_conn = $peer_conns{$hash2};
}
else {
$peer_conn = PeerConn->new(local_addr => $pkt->receiver(),
peer_addr => $pkt->transmitter());
$peer_conns{$hash} = $peer_conn;
}
}
$peer_conn->add_pkt($pkt);
$last_pkt = $pkt;
#print "pkt: rcvr: " . $pkt->receiver() . " transmitter: " . $pkt->transmitter() . "\n";
}