mirror of
https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
synced 2025-11-02 03:37:55 +00:00
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:
150
wifi_diag/Packet.pm
Normal file
150
wifi_diag/Packet.pm
Normal 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
80
wifi_diag/PeerConn.pm
Normal 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
42
wifi_diag/Tid.pm
Normal 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
89
wifi_diag/wifi_pcap_diag.pl
Executable 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";
|
||||
}
|
||||
Reference in New Issue
Block a user