diff --git a/wifi_diag/Packet.pm b/wifi_diag/Packet.pm new file mode 100644 index 00000000..dc1f2e9c --- /dev/null +++ b/wifi_diag/Packet.pm @@ -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; diff --git a/wifi_diag/PeerConn.pm b/wifi_diag/PeerConn.pm new file mode 100644 index 00000000..57b6010f --- /dev/null +++ b/wifi_diag/PeerConn.pm @@ -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; diff --git a/wifi_diag/Tid.pm b/wifi_diag/Tid.pm new file mode 100644 index 00000000..5db3159c --- /dev/null +++ b/wifi_diag/Tid.pm @@ -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; diff --git a/wifi_diag/wifi_pcap_diag.pl b/wifi_diag/wifi_pcap_diag.pl new file mode 100755 index 00000000..d1a9982a --- /dev/null +++ b/wifi_diag/wifi_pcap_diag.pl @@ -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"; +}