Add support for 802.11 A-MSDU aggregates

This commit is contained in:
Tim Wojtulewicz 2023-04-14 15:01:14 -07:00 committed by Tim Wojtulewicz
parent 5b1c6216bd
commit 2d05beac06
8 changed files with 127 additions and 9 deletions

View file

@ -3,6 +3,7 @@
#include "zeek/TunnelEncapsulation.h"
#include "zeek/Conn.h"
#include "zeek/Reporter.h"
#include "zeek/util.h"
namespace zeek
@ -56,4 +57,15 @@ bool operator==(const EncapsulationStack& e1, const EncapsulationStack& e2)
return true;
}
void EncapsulationStack::Pop()
{
if ( Depth() == 0 )
{
reporter->InternalWarning("Attempted to pop from empty EncapsulationStack\n");
return;
}
conns->pop_back();
}
} // namespace zeek

View file

@ -249,6 +249,11 @@ public:
return nullptr;
}
/**
* Pops the last element off the encapsulation stack.
*/
void Pop();
protected:
std::vector<EncapsulatingConn>* conns;
};

View file

@ -17,6 +17,7 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
}
u_char fc_80211 = data[0]; // Frame Control field
bool is_amsdu = false;
// Skip non-data frame types (management & control).
if ( ! ((fc_80211 >> 2) & 0x02) )
@ -33,9 +34,9 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
// Look for the QoS indicator bit.
if ( (fc_80211 >> 4) & 0x08 )
{
// Skip in case of A-MSDU subframes indicated by QoS control field.
if ( data[len_80211] & 0x80 )
return false;
// Store off whether this is an A-MSDU header, which indicates that there are
// mulitple packets following the 802.11 header.
is_amsdu = (data[len_80211] & 0x80) == 0x80;
// Check for the protected bit. This means the data is encrypted and we can't
// do anything with it.
@ -75,13 +76,78 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
break;
}
// skip 802.11 data header
// skip the 802.11 data header
data += len_80211;
len -= len_80211;
len_80211 += 8;
if ( len_80211 >= len )
if ( ! is_amsdu )
{
Weird("truncated_802_11_header", packet);
return HandleInnerPacket(len, data, packet);
}
else
{
size_t amsdu_padding = 0;
size_t encap_index = packet->encap ? packet->encap->Depth() : 0;
while ( len > 0 )
{
if ( len < 14 )
{
Weird("truncated_802_11_amsdu_header", packet);
return false;
}
// This is the length of everything after the A-MSDU subframe header.
size_t amsdu_len = (data[12] << 8) + data[13];
if ( len < amsdu_len )
{
Weird("truncated_802_11_amsdu_packet", packet);
return false;
}
// Skip the A-MSDU subframe header. This should place us at the start of an LLC header.
data += 14;
len -= 14;
if ( ! HandleInnerPacket(amsdu_len, data, packet) )
{
Weird("invalid_802_11_amsdu_inner_packet", packet);
return false;
}
data += amsdu_len;
len -= amsdu_len;
// Each A-MSDU subframe is padded by up to 3 bytes to make a multiple of 4. This padding
// isn't included in the length field value. The padding also doesn't happen with the
// last subframe, so check to see that we can even subtract it. Unfortunately, there
// isn't a frame counter in the header so we just have trust that it all works out.
amsdu_padding = amsdu_len % 4;
if ( len >= amsdu_padding )
{
data += amsdu_padding;
len -= amsdu_padding;
}
// Pop encapsuations back up to the level where we started processing so that the next
// subframe gets the same encapsulation stack.
if ( packet->encap )
{
while ( packet->encap->Depth() > encap_index )
packet->encap->Pop();
}
}
return true;
}
}
bool IEEE802_11Analyzer::HandleInnerPacket(size_t len, const uint8_t* data, Packet* packet) const
{
// Make sure there's room for an LLC header.
if ( len < 8 )
{
Weird("truncated_802_11_llc_header", packet);
return false;
}
@ -92,6 +158,7 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
data[5] == 0 )
{
data += 6;
len -= 6;
}
else
{
@ -100,11 +167,13 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
return false;
}
// Get the protocol and skip the rest of the LLC header.
uint32_t protocol = (data[0] << 8) + data[1];
data += 2;
len -= 2;
if ( packet->tunnel_type == BifEnum::Tunnel::NONE )
return ForwardPacket(len - len_80211, data, packet, protocol);
return ForwardPacket(len, data, packet, protocol);
else
{
// For tunneled packets, reset the packet's protocol based on the one in the LLC header.
@ -114,6 +183,6 @@ bool IEEE802_11Analyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet*
else if ( protocol == 0x86DD )
packet->proto = IPPROTO_IPV6;
return ForwardPacket(len - len_80211, data, packet, packet->proto);
return ForwardPacket(len, data, packet, packet->proto);
}
}

View file

@ -20,6 +20,9 @@ public:
{
return std::make_shared<IEEE802_11Analyzer>();
}
private:
bool HandleInnerPacket(size_t len, const uint8_t* data, Packet* packet) const;
};
}

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents
#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string]
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h 157.240.18.16 443 149.159.130.184 49392 tcp - - - - OTH F F 0 D 2 356 0 0 CHhAvVGS1DHFjwGM9
#close XXXX-XX-XX-XX-XX-XX

View file

@ -0,0 +1,11 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path tunnel
#open XXXX-XX-XX-XX-XX-XX
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p tunnel_type action
#types time string addr port addr port enum enum
XXXXXXXXXX.XXXXXX CHhAvVGS1DHFjwGM9 10.226.22.9 0 10.102.16.187 0 Tunnel::GRE Tunnel::DISCOVER
#close XXXX-XX-XX-XX-XX-XX

Binary file not shown.

View file

@ -0,0 +1,7 @@
# @TEST-DOC: Tests a GRE ARUBA trace that contains IEEE 802.11 QoS A-MSDU headers. This is testing that the tunnel is detected and that the conn byte size contains both A-MSDU subframe packets.
# @TEST-EXEC: zeek -C -b -r $TRACES/tunnels/gre-aruba-amsdu.pcap %INPUT
# @TEST-EXEC: btest-diff tunnel.log
# @TEST-EXEC: btest-diff conn.log
@load base/protocols/conn
@load base/frameworks/tunnels