Initial implementation of Lower-Level analyzers

This commit is contained in:
Peter Oettig 2019-05-09 17:49:52 +02:00 committed by Tim Wojtulewicz
parent f744d4c070
commit b2e6c9ac9a
146 changed files with 3967 additions and 613 deletions

2
.gitignore vendored
View file

@ -4,7 +4,7 @@ tmp
# Configuration and build directories for CLion
.idea
cmake-build-debug
cmake-build-*
# skip DS Store for MacOS
.DS_Store

View file

@ -5341,3 +5341,30 @@ event net_done(t: time)
# execution would be another idea.
@if ( __init_primary_bifs() )
@endif
module LLAnalyzer;
# Defines a mapping for the LLAnalyzer's configuration tree. This
# maps from a parent analyzer to a child analyzer through a numeric
# identifier.
export {
type ConfigEntry : record {
# The parent analyzer. This analyzer will check for the *identifier* in the
# packet data to know whether to call the next analyzer. This field is optional.
# If it is not included, the identifier will attach to the "root" analyzer. This
# means that the identifier will be searched for the initial packet header instead
# of later headers.
parent : LLAnalyzer::Tag &optional;
# A numeric identifier that can be found in the packet data that denotes an
# analyzer should be called.
identifier : count;
# The analyzer that corresponds to the above identifier.
analyzer : LLAnalyzer::Tag;
};
const config_map : vector of LLAnalyzer::ConfigEntry &redef;
}
@load base/llprotocols

View file

@ -0,0 +1,11 @@
@load base/llprotocols/default
@load base/llprotocols/ethernet
@load base/llprotocols/fddi
@load base/llprotocols/ieee802_11
@load base/llprotocols/ieee802_11_radio
@load base/llprotocols/linux_sll
@load base/llprotocols/nflog
@load base/llprotocols/null
@load base/llprotocols/ppp_serial
@load base/llprotocols/pppoe
@load base/llprotocols/vlan

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,6 @@
module LL_DEFAULT;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_DEFAULTANALYZER, $identifier=4, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_DEFAULTANALYZER, $identifier=6, $analyzer=LLAnalyzer::LLANALYZER_IPV6)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,16 @@
module LL_ETHERNET;
const DLT_EN10MB : count = 1;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_EN10MB, $analyzer=LLAnalyzer::LLANALYZER_ETHERNET),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x8847, $analyzer=LLAnalyzer::LLANALYZER_MPLS),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x0800, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x86DD, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x0806, $analyzer=LLAnalyzer::LLANALYZER_ARP),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x8035, $analyzer=LLAnalyzer::LLANALYZER_ARP),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x8100, $analyzer=LLAnalyzer::LLANALYZER_VLAN),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x88A8, $analyzer=LLAnalyzer::LLANALYZER_VLAN),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x9100, $analyzer=LLAnalyzer::LLANALYZER_VLAN),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_ETHERNET, $identifier=0x8864, $analyzer=LLAnalyzer::LLANALYZER_PPPOE)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,7 @@
module LL_FDDI;
const DLT_FDDI : count = 10;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_FDDI, $analyzer=LLAnalyzer::LLANALYZER_FDDI)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,11 @@
module LL_IEEE802_11;
const DLT_IEEE802_11 : count = 105;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_IEEE802_11, $analyzer=LLAnalyzer::LLANALYZER_IEEE802_11),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_IEEE802_11, $identifier=0x0800, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_IEEE802_11, $identifier=0x86DD, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_IEEE802_11, $identifier=0x0806, $analyzer=LLAnalyzer::LLANALYZER_ARP),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_IEEE802_11, $identifier=0x8035, $analyzer=LLAnalyzer::LLANALYZER_ARP)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,9 @@
module LL_IEEE802_11_RADIO;
const DLT_IEEE802_11_RADIO : count = 127;
const DLT_IEEE802_11 : count = 105;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_IEEE802_11_RADIO, $analyzer=LLAnalyzer::LLANALYZER_IEEE802_11_RADIO),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_IEEE802_11_RADIO, $identifier=DLT_IEEE802_11, $analyzer=LLAnalyzer::LLANALYZER_IEEE802_11)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,12 @@
module LL_LINUX_SLL;
const DLT_LINUX_SLL : count = 113;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_LINUX_SLL, $analyzer=LLAnalyzer::LLANALYZER_LINUXSLL),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_LINUXSLL, $identifier=0x0800, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_LINUXSLL, $identifier=0x86DD, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_LINUXSLL, $identifier=0x0806, $analyzer=LLAnalyzer::LLANALYZER_ARP),
# RARP
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_LINUXSLL, $identifier=0x8035, $analyzer=LLAnalyzer::LLANALYZER_ARP)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,11 @@
module LL_NFLOG;
const DLT_NFLOG : count = 239;
const AF_INET : count = 2;
const AF_INET6 : count = 10;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_NFLOG, $analyzer=LLAnalyzer::LLANALYZER_NFLOG),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NFLOG, $identifier=AF_INET, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NFLOG, $identifier=AF_INET6, $analyzer=LLAnalyzer::LLANALYZER_IPV6)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,19 @@
module LL_NULL;
const DLT_NULL : count = 0;
const AF_INET : count = 2;
const AF_INET6 : count = 10;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_NULL, $analyzer=LLAnalyzer::LLANALYZER_NULL),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NULL, $identifier=AF_INET, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
## From the Wireshark Wiki: AF_INET6ANALYZER, unfortunately, has different values in
## {NetBSD,OpenBSD,BSD/OS}, {FreeBSD,DragonFlyBSD}, and {Darwin/Mac OS X}, so an IPv6
## packet might have a link-layer header with 24, 28, or 30 as the AF_ value. As we
## may be reading traces captured on platforms other than what we're running on, we
## accept them all here.
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NULL, $identifier=24, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NULL, $identifier=28, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_NULL, $identifier=30, $analyzer=LLAnalyzer::LLANALYZER_IPV6)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,10 @@
module LL_PPP_SERIAL;
const DLT_PPP_SERIAL : count = 50;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($identifier=DLT_PPP_SERIAL, $analyzer=LLAnalyzer::LLANALYZER_PPPSERIAL),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_PPPSERIAL, $identifier=0x0281, $analyzer=LLAnalyzer::LLANALYZER_MPLS),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_PPPSERIAL, $identifier=0x0021, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_PPPSERIAL, $identifier=0x0057, $analyzer=LLAnalyzer::LLANALYZER_IPV6)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,6 @@
module LL_PPPOE;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_PPPOE, $identifier=0x0021, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_PPPOE, $identifier=0x0057, $analyzer=LLAnalyzer::LLANALYZER_IPV6)
};

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,11 @@
module LL_VLAN;
redef LLAnalyzer::config_map += {
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x8847, $analyzer=LLAnalyzer::LLANALYZER_MPLS),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x0800, $analyzer=LLAnalyzer::LLANALYZER_IPV4),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x86DD, $analyzer=LLAnalyzer::LLANALYZER_IPV6),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x0806, $analyzer=LLAnalyzer::LLANALYZER_ARP),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x8035, $analyzer=LLAnalyzer::LLANALYZER_ARP),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x8100, $analyzer=LLAnalyzer::LLANALYZER_VLAN),
LLAnalyzer::ConfigEntry($parent=LLAnalyzer::LLANALYZER_VLAN, $identifier=0x8864, $analyzer=LLAnalyzer::LLANALYZER_PPPOE)
};

View file

@ -146,6 +146,7 @@ set(bro_PLUGIN_LIBS CACHE INTERNAL "plugin libraries" FORCE)
set(bro_PLUGIN_DEPS CACHE INTERNAL "plugin dependencies" FORCE)
add_subdirectory(analyzer)
add_subdirectory(llanalyzer)
add_subdirectory(broker)
add_subdirectory(zeekygen)
add_subdirectory(file_analysis)

View file

@ -349,8 +349,8 @@ protected:
TransportProto proto;
uint32_t orig_flow_label, resp_flow_label; // most recent IPv6 flow labels
uint32_t vlan, inner_vlan; // VLAN this connection traverses, if available
u_char orig_l2_addr[Packet::l2_addr_len]; // Link-layer originator address, if available
u_char resp_l2_addr[Packet::l2_addr_len]; // Link-layer responder address, if available
u_char orig_l2_addr[Packet::L2_ADDR_LEN]; // Link-layer originator address, if available
u_char resp_l2_addr[Packet::L2_ADDR_LEN]; // Link-layer responder address, if available
double start_time, last_time;
double inactivity_timeout;
RecordValPtr conn_val;

View file

@ -19,18 +19,19 @@ DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
{ "string", 0, false },
{ "notifiers", 0, false },
{ "main-loop", 0, false },
{ "llanalyzer", 0, false },
{ "dpd", 0, false },
{ "tm", 0, false },
{ "logging", 0, false },
{"input", 0, false },
{ "input", 0, false },
{ "threading", 0, false },
{ "file_analysis", 0, false },
{ "plugins", 0, false },
{ "zeekygen", 0, false },
{ "pktio", 0, false },
{ "broker", 0, false },
{ "scripts", 0, false},
{ "supervisor", 0, false}
{ "scripts", 0, false },
{ "supervisor", 0, false }
};
DebugLogger::DebugLogger()

View file

@ -35,6 +35,7 @@ enum DebugStream {
DBG_STRING, // String code
DBG_NOTIFIERS, // Notifiers
DBG_MAINLOOP, // Main IOSource loop
DBG_LLANALYZER, // Low-Layer Analyzer Proof of Concept
DBG_ANALYZER, // Analyzer framework
DBG_TM, // Time-machine packet input via Brocolli
DBG_LOGGING, // Logging streams

View file

@ -188,7 +188,6 @@ void NetSessions::NextPacket(double t, const Packet* pkt)
return;
}
if ( dump_this_packet && ! zeek::detail::record_all_packets )
DumpPacket(pkt);
}

View file

@ -3,6 +3,7 @@
#include "Desc.h"
#include "IP.h"
#include "iosource/Manager.h"
#include "llanalyzer/Manager.h"
#include "Var.h"
extern "C" {
@ -45,7 +46,7 @@ void Packet::Init(int arg_link_type, pkt_timeval *arg_ts, uint32_t arg_caplen,
data = arg_data;
time = ts.tv_sec + double(ts.tv_usec) / 1e6;
hdr_size = GetLinkHeaderSize(arg_link_type);
hdr_size = 0;
eth_type = 0;
vlan = 0;
inner_vlan = 0;
@ -58,14 +59,18 @@ void Packet::Init(int arg_link_type, pkt_timeval *arg_ts, uint32_t arg_caplen,
l3_proto = L3_UNKNOWN;
l3_checksummed = false;
if ( data && cap_len < hdr_size )
{
Weird("truncated_link_header");
return;
}
// For ll-analyzer: cur_pos points to the next payload.
cur_pos = data;
if ( data )
ProcessLayer2();
{
// From here we assume that layer 2 is valid. If an ll-analyzer encounters
// an issue, it will call Packet::Weird(), which sets l2_valid to false.
l2_valid = true;
llanalyzer_mgr->ProcessPacket(this);
// Calculate header size after processing lower layers.
hdr_size = cur_pos - data;
}
}
const IP_Hdr Packet::IP() const
@ -79,521 +84,12 @@ void Packet::Weird(const char* name)
l2_valid = false;
}
int Packet::GetLinkHeaderSize(int link_type)
const u_char* const Packet::GetEndOfData() const
{
switch ( link_type ) {
case DLT_NULL:
return 4;
case DLT_EN10MB:
return 14;
case DLT_FDDI:
return 13 + 8; // fddi_header + LLC
#ifdef DLT_LINUX_SLL
case DLT_LINUX_SLL:
return 16;
#endif
case DLT_PPP_SERIAL: // PPP_SERIAL
return 4;
case DLT_IEEE802_11: // 802.11 monitor
return 34;
case DLT_IEEE802_11_RADIO: // 802.11 plus RadioTap
return 59;
case DLT_NFLOG:
// Linux netlink NETLINK NFLOG socket log messages
// The actual header size is variable, but we return the minimum
// expected size here, which is 4 bytes for the main header plus at
// least 2 bytes each for the type and length values assoicated with
// the final TLV carrying the packet payload.
return 8;
case DLT_RAW:
return 0;
return data + cap_len;
}
return -1;
}
void Packet::ProcessLayer2()
{
l2_valid = true;
// Unfortunately some packets on the link might have MPLS labels
// while others don't. That means we need to ask the link-layer if
// labels are in place.
bool have_mpls = false;
const u_char* pdata = data;
const u_char* end_of_data = data + cap_len;
switch ( link_type ) {
case DLT_NULL:
{
int protocol = (pdata[3] << 24) + (pdata[2] << 16) + (pdata[1] << 8) + pdata[0];
pdata += GetLinkHeaderSize(link_type);
// From the Wireshark Wiki: "AF_INET6, unfortunately, has
// different values in {NetBSD,OpenBSD,BSD/OS},
// {FreeBSD,DragonFlyBSD}, and {Darwin/Mac OS X}, so an IPv6
// packet might have a link-layer header with 24, 28, or 30
// as the AF_ value." As we may be reading traces captured on
// platforms other than what we're running on, we accept them
// all here.
if ( protocol == AF_INET )
l3_proto = L3_IPV4;
else if ( protocol == 24 || protocol == 28 || protocol == 30 )
l3_proto = L3_IPV6;
else
{
Weird("non_ip_packet_in_null_transport");
return;
}
break;
}
case DLT_EN10MB:
{
// Skip past Cisco FabricPath to encapsulated ethernet frame.
if ( pdata[12] == 0x89 && pdata[13] == 0x03 )
{
auto constexpr cfplen = 16;
if ( pdata + cfplen + GetLinkHeaderSize(link_type) >= end_of_data )
{
Weird("truncated_link_header_cfp");
return;
}
pdata += cfplen;
}
// Get protocol being carried from the ethernet frame.
int protocol = (pdata[12] << 8) + pdata[13];
eth_type = protocol;
l2_dst = pdata;
l2_src = pdata + 6;
pdata += GetLinkHeaderSize(link_type);
bool saw_vlan = false;
while ( protocol == 0x8100 || protocol == 0x9100 ||
protocol == 0x8864 )
{
switch ( protocol )
{
// VLAN carried over the ethernet frame.
// 802.1q / 802.1ad
case 0x8100:
case 0x9100:
{
if ( pdata + 4 >= end_of_data )
{
Weird("truncated_link_header");
return;
}
auto& vlan_ref = saw_vlan ? inner_vlan : vlan;
vlan_ref = ((pdata[0] << 8) + pdata[1]) & 0xfff;
protocol = ((pdata[2] << 8) + pdata[3]);
pdata += 4; // Skip the vlan header
saw_vlan = true;
eth_type = protocol;
}
break;
// PPPoE carried over the ethernet frame.
case 0x8864:
{
if ( pdata + 8 >= end_of_data )
{
Weird("truncated_link_header");
return;
}
protocol = (pdata[6] << 8) + pdata[7];
pdata += 8; // Skip the PPPoE session and PPP header
if ( protocol == 0x0021 )
l3_proto = L3_IPV4;
else if ( protocol == 0x0057 )
l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
Weird("non_ip_packet_in_pppoe_encapsulation");
return;
}
}
break;
}
}
// Check for MPLS in VLAN.
if ( protocol == 0x8847 )
have_mpls = true;
// Normal path to determine Layer 3 protocol.
if ( ! have_mpls && l3_proto == L3_UNKNOWN )
{
if ( protocol == 0x800 )
l3_proto = L3_IPV4;
else if ( protocol == 0x86dd )
l3_proto = L3_IPV6;
else if ( protocol == 0x0806 || protocol == 0x8035 )
l3_proto = L3_ARP;
else
{
// Neither IPv4 nor IPv6.
Weird("non_ip_packet_in_ethernet");
return;
}
}
break;
}
case DLT_PPP_SERIAL:
{
// Get PPP protocol.
int protocol = (pdata[2] << 8) + pdata[3];
pdata += GetLinkHeaderSize(link_type);
if ( protocol == 0x0281 )
{
// MPLS Unicast. Remove the pdata link layer and
// denote a header size of zero before the IP header.
have_mpls = true;
}
else if ( protocol == 0x0021 )
l3_proto = L3_IPV4;
else if ( protocol == 0x0057 )
l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
Weird("non_ip_packet_in_ppp_encapsulation");
return;
}
break;
}
case DLT_IEEE802_11_RADIO:
{
if ( pdata + 3 >= end_of_data )
{
Weird("truncated_radiotap_header");
return;
}
// Skip over the RadioTap header
int rtheader_len = (pdata[3] << 8) + pdata[2];
if ( pdata + rtheader_len >= end_of_data )
{
Weird("truncated_radiotap_header");
return;
}
pdata += rtheader_len;
// fallthrough
}
case DLT_IEEE802_11:
{
u_char len_80211 = 24; // minimal length of data frames
if ( pdata + len_80211 >= end_of_data )
{
Weird("truncated_802_11_header");
return;
}
u_char fc_80211 = pdata[0]; // Frame Control field
// Skip non-data frame types (management & control).
if ( ! ((fc_80211 >> 2) & 0x02) )
return;
// Skip subtypes without data.
if ( (fc_80211 >> 4) & 0x04 )
return;
// 'To DS' and 'From DS' flags set indicate use of the 4th
// address field.
if ( (pdata[1] & 0x03) == 0x03 )
len_80211 += l2_addr_len;
// Look for the QoS indicator bit.
if ( (fc_80211 >> 4) & 0x08 )
{
// Skip in case of A-MSDU subframes indicated by QoS
// control field.
if ( pdata[len_80211] & 0x80)
return;
len_80211 += 2;
}
if ( pdata + len_80211 >= end_of_data )
{
Weird("truncated_802_11_header");
return;
}
// Determine link-layer addresses based
// on 'To DS' and 'From DS' flags
switch ( pdata[1] & 0x03 ) {
case 0x00:
l2_src = pdata + 10;
l2_dst = pdata + 4;
break;
case 0x01:
l2_src = pdata + 10;
l2_dst = pdata + 16;
break;
case 0x02:
l2_src = pdata + 16;
l2_dst = pdata + 4;
break;
case 0x03:
l2_src = pdata + 24;
l2_dst = pdata + 16;
break;
}
// skip 802.11 data header
pdata += len_80211;
if ( pdata + 8 >= end_of_data )
{
Weird("truncated_802_11_header");
return;
}
// Check that the DSAP and SSAP are both SNAP and that the control
// field indicates that this is an unnumbered frame.
// The organization code (24bits) needs to also be zero to
// indicate that this is encapsulated ethernet.
if ( pdata[0] == 0xAA && pdata[1] == 0xAA && pdata[2] == 0x03 &&
pdata[3] == 0 && pdata[4] == 0 && pdata[5] == 0 )
{
pdata += 6;
}
else
{
// If this is a logical link control frame without the
// possibility of having a protocol we care about, we'll
// just skip it for now.
return;
}
int protocol = (pdata[0] << 8) + pdata[1];
if ( protocol == 0x0800 )
l3_proto = L3_IPV4;
else if ( protocol == 0x86DD )
l3_proto = L3_IPV6;
else if ( protocol == 0x0806 || protocol == 0x8035 )
l3_proto = L3_ARP;
else
{
Weird("non_ip_packet_in_ieee802_11");
return;
}
pdata += 2;
break;
}
case DLT_NFLOG:
{
// See https://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html
uint8_t protocol = pdata[0];
if ( protocol == AF_INET )
l3_proto = L3_IPV4;
else if ( protocol == AF_INET6 )
l3_proto = L3_IPV6;
else
{
Weird("non_ip_in_nflog");
return;
}
uint8_t version = pdata[1];
if ( version != 0 )
{
Weird("unknown_nflog_version");
return;
}
// Skip to TLVs.
pdata += 4;
uint16_t tlv_len;
uint16_t tlv_type;
while ( true )
{
if ( pdata + 4 >= end_of_data )
{
Weird("nflog_no_pcap_payload");
return;
}
// TLV Type and Length values are specified in host byte order
// (libpcap should have done any needed byteswapping already).
tlv_len = *(reinterpret_cast<const uint16_t*>(pdata));
tlv_type = *(reinterpret_cast<const uint16_t*>(pdata + 2));
auto constexpr nflog_type_payload = 9;
if ( tlv_type == nflog_type_payload )
{
// The raw packet payload follows this TLV.
pdata += 4;
break;
}
else
{
// The Length value includes the 4 octets for the Type and
// Length values, but TLVs are also implicitly padded to
// 32-bit alignments (that padding may not be included in
// the Length value).
if ( tlv_len < 4 )
{
Weird("nflog_bad_tlv_len");
return;
}
else
{
auto rem = tlv_len % 4;
if ( rem != 0 )
tlv_len += 4 - rem;
}
pdata += tlv_len;
}
}
break;
}
default:
{
// Assume we're pointing at IP. Just figure out which version.
pdata += GetLinkHeaderSize(link_type);
if ( pdata + sizeof(struct ip) >= end_of_data )
{
Weird("truncated_link_header");
return;
}
const struct ip* ip = (const struct ip *)pdata;
if ( ip->ip_v == 4 )
l3_proto = L3_IPV4;
else if ( ip->ip_v == 6 )
l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
Weird("non_ip_packet");
return;
}
break;
}
}
if ( have_mpls )
{
// Skip the MPLS label stack.
bool end_of_stack = false;
while ( ! end_of_stack )
{
if ( pdata + 4 >= end_of_data )
{
Weird("truncated_link_header");
return;
}
end_of_stack = *(pdata + 2) & 0x01;
pdata += 4;
}
// We assume that what remains is IP
if ( pdata + sizeof(struct ip) >= end_of_data )
{
Weird("no_ip_in_mpls_payload");
return;
}
const struct ip* ip = (const struct ip *)pdata;
if ( ip->ip_v == 4 )
l3_proto = L3_IPV4;
else if ( ip->ip_v == 6 )
l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
Weird("no_ip_in_mpls_payload");
return;
}
}
else if ( zeek::detail::encap_hdr_size )
{
// Blanket encapsulation. We assume that what remains is IP.
if ( pdata + zeek::detail::encap_hdr_size + sizeof(struct ip) >= end_of_data )
{
Weird("no_ip_left_after_encap");
return;
}
pdata += zeek::detail::encap_hdr_size;
const struct ip* ip = (const struct ip *)pdata;
if ( ip->ip_v == 4 )
l3_proto = L3_IPV4;
else if ( ip->ip_v == 6 )
l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
Weird("no_ip_in_encap");
return;
}
}
// We've now determined (a) L3_IPV4 vs (b) L3_IPV6 vs (c) L3_ARP vs
// (d) L3_UNKNOWN.
// Calculate how much header we've used up.
hdr_size = (pdata - data);
}
RecordValPtr Packet::ToRawPktHdrVal() const
IntrusivePtr<RecordVal> Packet::ToRawPktHdrVal() const
{
static auto raw_pkt_hdr_type = id::find_type<RecordType>("raw_pkt_hdr");
static auto l2_hdr_type = id::find_type<RecordType>("l2_hdr");
@ -684,11 +180,31 @@ ValPtr Packet::FmtEUI48(const u_char* mac) const
}
void Packet::Describe(ODesc* d) const
{
switch ( l3_proto )
{
case L3_ARP:
d->Add("ARP");
break;
case L3_IPV4:
d->Add("IPv4");
break;
case L3_IPV6:
d->Add("IPv6");
break;
default:
d->Add("Unknown L3 protocol");
}
// Add IP-specific information
if ( l3_proto == L3_IPV4 || l3_proto == L3_IPV6 )
{
const IP_Hdr ip = IP();
d->Add(": ");
d->Add(ip.SrcAddr());
d->Add("->");
d->Add(ip.DstAddr());
}
}
} // namespace zeek

View file

@ -127,7 +127,7 @@ public:
/**
* Interprets the Layer 3 of the packet as IP and returns a
* correspondign object.
* corresponding object.
*/
const IP_Hdr IP() const;
@ -141,14 +141,11 @@ public:
RecordVal* BuildPktHdrVal() const;
/**
* Static method returning the link-layer header size for a given
* link type.
* Returns the end of the captured data for bound checking.
*
* @param link_type The link tyoe.
*
* @return The header size in bytes, or -1 if not known.
* @return End of the packet data.
*/
static int GetLinkHeaderSize(int link_type);
const u_char* const GetEndOfData() const;
/**
* Describes the packet, with standard signature.
@ -158,7 +155,14 @@ public:
/**
* Maximal length of a layer 2 address.
*/
static const int l2_addr_len = 6;
static const int L2_ADDR_LEN = 6;
/**
* Empty layer 2 address to be used as default value. For example, the
* LinuxSLL llanalyzer doesn't have a destination address in the header
* and thus sets it to this default address.
*/
static constexpr const u_char L2_EMPTY_ADDR[L2_ADDR_LEN] = { 0 };
// These are passed in through the constructor.
std::string tag; /// Used in serialization
@ -168,6 +172,7 @@ public:
uint32_t len; /// Actual length on wire
uint32_t cap_len; /// Captured packet length
uint32_t link_type; /// pcap link_type (DLT_EN10MB, DLT_RAW, etc)
const uint8_t* cur_pos; /// Pointer to the current start of unanalyzed payload data in the raw packet, used by llanalyzers
// These are computed from Layer 2 data. These fields are only valid if
// Layer2Valid() returns true.
@ -224,13 +229,10 @@ public:
*/
bool l3_checksummed;
private:
// Calculate layer 2 attributes.
void ProcessLayer2();
// Wrapper to generate a packet-level weird.
// Wrapper to generate a packet-level weird. Has to be public for llanalyzers to use it.
void Weird(const char* name);
private:
// Renders an MAC address into its ASCII representation.
ValPtr FmtEUI48(const u_char* mac) const;

View file

@ -53,11 +53,6 @@ const char* PktDumper::ErrorMsg() const
return errmsg.size() ? errmsg.c_str() : nullptr;
}
int PktDumper::HdrSize() const
{
return is_open ? props.hdr_size : -1;
}
void PktDumper::Opened(const Properties& arg_props)
{
is_open = true;

View file

@ -50,11 +50,6 @@ public:
*/
const char* ErrorMsg() const;
/**
* Returns the size of the link-layer headers with this dumper.
*/
int HdrSize() const;
// PktDumper interface for derived classes to implement.
/**
@ -97,7 +92,6 @@ protected:
*/
struct Properties {
std::string path;
int hdr_size;
double open_time;
};

View file

@ -90,16 +90,6 @@ double PktSrc::CurrentPacketWallClock()
void PktSrc::Opened(const Properties& arg_props)
{
if ( Packet::GetLinkHeaderSize(arg_props.link_type) < 0 )
{
char buf[512];
snprintf(buf, sizeof(buf),
"unknown data link type 0x%x", arg_props.link_type);
Error(buf);
Close();
return;
}
props = arg_props;
SetClosed(false);

View file

@ -82,7 +82,6 @@ void PcapDumper::Open()
}
props.open_time = run_state::network_time;
props.hdr_size = Packet::GetLinkHeaderSize(pcap_datalink(pd));
Opened(props);
}

View file

@ -0,0 +1,47 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <algorithm>
#include "Analyzer.h"
namespace zeek::llanalyzer {
Analyzer::Analyzer(std::string name)
{
Tag t = llanalyzer_mgr->GetComponentTag(name);
if ( ! t )
reporter->InternalError("unknown llanalyzer name %s", name.c_str());
Init(t);
}
Analyzer::Analyzer(const Tag& tag)
{
Init(tag);
}
/* PRIVATE */
void Analyzer::Init(const Tag& _tag)
{
tag = _tag;
}
const Tag Analyzer::GetAnalyzerTag() const
{
assert(tag);
return tag;
}
const char* Analyzer::GetAnalyzerName() const
{
assert(tag);
return llanalyzer_mgr->GetComponentName(tag).c_str();
}
bool Analyzer::IsAnalyzer(const char* name)
{
assert(tag);
return llanalyzer_mgr->GetComponentName(tag).compare(name) == 0;
}
}

89
src/llanalyzer/Analyzer.h Normal file
View file

@ -0,0 +1,89 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "Defines.h"
#include "Manager.h"
#include "Tag.h"
#include <iosource/Packet.h>
namespace zeek::llanalyzer {
/**
* Result of low layer analysis.
*/
enum class AnalyzerResult {
Failed, // Analysis failed
Continue, // Analysis succeded and an encapuslated protocol was determined
Terminate // Analysis succeded and there is no further analysis to do
};
using AnalysisResultTuple = std::tuple<AnalyzerResult, identifier_t>;
class Analyzer {
public:
/**
* Constructor.
*
* @param name The name for the type of analyzer. The name must match
* the one the corresponding Component registers.
*/
explicit Analyzer(std::string name);
/**
* Constructor.
*
* @param tag The tag for the type of analyzer. The tag must map to
* the name the corresponding Component registers.
*/
explicit Analyzer(const Tag& tag);
/**
* Destructor.
*/
virtual ~Analyzer() = default;
/**
* Returns the tag associated with the analyzer's type.
*/
const Tag GetAnalyzerTag() const;
/**
* Returns a textual description of the analyzer's type. This is
* what's passed to the constructor and usally corresponds to the
* protocol name, e.g., "ARP".
*/
const char* GetAnalyzerName() const;
/**
* Returns true if this analyzer's type matches the name passes in.
* This is shortcut for comparing GetAnalyzerName() with the given
* name.
*
* @param name The name to check.
*/
bool IsAnalyzer(const char* name);
/**
* Analyzes the given packet. The analysis is supposed to start at cur_pos
* of the packet, which points to the so far unanalyzed part of the packet.
* If the analyzed protocol encapsulates another protocol, the packet's
* cur_pos should be updated to point to that payload.
*
* @param packet The packet to analyze.
*
* @return A tuple of analysis result and identifier. The result indicates
* how to proceed. If analysis can continue, the identifier determines the
* encapsulated protocol.
*/
virtual std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) = 0;
protected:
friend class Manager;
private:
Tag tag;
void Init(const Tag& tag);
};
} // llanalyzer namespace end

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "Analyzer.h"
#include "Defines.h"
namespace zeek::llanalyzer {
class Analyzer;
class AnalyzerSet {
public:
virtual ~AnalyzerSet() = default;
virtual Analyzer* Dispatch(identifier_t identifier) = 0;
virtual void Reset() = 0;
protected:
friend class Manager;
virtual void DumpDebug() const = 0;
};
}

View file

@ -0,0 +1,21 @@
include(ZeekSubdir)
include_directories(BEFORE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
add_subdirectory(protocol)
add_subdirectory(dispatchers)
set(llanalyzer_SRCS
Analyzer.cc
ProtocolAnalyzerSet.cc
Manager.cc
Component.cc
Tag.cc
Config.cc
)
bro_add_subdir_library(llanalyzer ${llanalyzer_SRCS})
add_dependencies(bro_llanalyzer generate_outputs)

View file

@ -0,0 +1,33 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Component.h"
#include "Desc.h"
#include "Manager.h"
using namespace zeek::llanalyzer;
Component::Component(const std::string& name, factory_callback arg_factory, Tag::subtype_t arg_subtype, bool arg_enabled)
: plugin::Component(plugin::component::LLANALYZER, name),
plugin::TaggedComponent<llanalyzer::Tag>(arg_subtype)
{
factory = arg_factory;
enabled = arg_enabled;
}
void Component::Initialize()
{
InitializeTag();
llanalyzer_mgr->RegisterComponent(this, "LLANALYZER_");
}
void Component::DoDescribe(ODesc* d) const
{
if ( factory )
{
d->Add("LLANALYZER_");
d->Add(CanonicalName());
d->Add(", ");
}
d->Add(enabled ? "enabled" : "disabled");
}

View file

@ -0,0 +1,61 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "zeek-config.h"
#include "util.h"
#include "Tag.h"
#include "plugin/Component.h"
#include "plugin/TaggedComponent.h"
namespace zeek::llanalyzer {
class Analyzer;
class Component : public plugin::Component,
public plugin::TaggedComponent<llanalyzer::Tag> {
public:
typedef Analyzer* (*factory_callback)();
Component(const std::string& name, factory_callback factory, Tag::subtype_t subtype = 0, bool enabled = true);
~Component() override = default;
/**
* Initialization function. This function has to be called before any
* plugin component functionality is used; it is used to add the
* plugin component to the list of components and to initialize tags
*/
void Initialize() override;
/**
* Returns the analyzer's factory function.
*/
factory_callback Factory() const { return factory; }
/**
* Returns true if the analyzer is currently enabled and hence
* available for use.
*/
bool Enabled() const { return enabled; }
/**
* Enables or disables this analyzer.
*
* @param arg_enabled True to enabled, false to disable.
*
*/
void SetEnabled(bool arg_enabled) { enabled = arg_enabled; }
protected:
/**
* Overriden from plugin::Component.
*/
void DoDescribe(ODesc* d) const override;
private:
factory_callback factory; // The analyzer's factory callback.
bool enabled; // True if the analyzer is enabled.
};
}

87
src/llanalyzer/Config.cc Normal file
View file

@ -0,0 +1,87 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Config.h"
#include "Reporter.h"
#include "DebugLogger.h"
namespace zeek::llanalyzer {
// ##############################
// ####### DispatcherConfig #####
// ##############################
const std::string& DispatcherConfig::GetName() const
{
return name;
}
const std::map<identifier_t, std::string>& DispatcherConfig::GetMappings() const
{
return mappings;
}
void DispatcherConfig::AddMapping(identifier_t identifier,
const std::string& analyzer_name)
{
DBG_LOG(DBG_LLANALYZER, "Adding configuration mapping: %s -> %#x -> %s",
name.c_str(), identifier, analyzer_name.c_str());
if ( mappings.count(identifier) )
reporter->InternalError("Invalid config, identifier %#x already exists "
"for dispatcher set %s.",
identifier, name.c_str());
mappings.emplace(identifier, analyzer_name);
}
bool DispatcherConfig::operator==(const DispatcherConfig& rhs) const
{
return name == rhs.name;
}
bool DispatcherConfig::operator!=(const DispatcherConfig& rhs) const
{
return ! (rhs == *this);
}
// ##############################
// ########### Config ###########
// ##############################
std::optional<std::reference_wrapper<DispatcherConfig>>
Config::GetDispatcherConfig(const std::string& name)
{
auto it = std::find_if(
dispatchers.begin(), dispatchers.end(),
[&](const DispatcherConfig& conf) {
return conf.GetName() == name;
});
if ( it == dispatchers.end() )
return {};
else
return {std::ref(*it)};
}
const std::vector<DispatcherConfig>& Config::GetDispatchers() const
{
return dispatchers;
}
DispatcherConfig& Config::AddDispatcherConfig(const std::string& name)
{
return dispatchers.emplace_back(name);
}
void Config::AddMapping(const std::string& name, identifier_t identifier,
const std::string& analyzer_name)
{
// Create dispatcher config if it does not exist yet
std::optional<std::reference_wrapper<DispatcherConfig>> dispatch_config =
GetDispatcherConfig(name);
if ( ! dispatch_config )
AddDispatcherConfig(name).AddMapping(identifier, analyzer_name);
else
dispatch_config->get().AddMapping(identifier, analyzer_name);
}
} // namespace llanalyzer

44
src/llanalyzer/Config.h Normal file
View file

@ -0,0 +1,44 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "Defines.h"
namespace zeek::llanalyzer {
class DispatcherConfig {
public:
explicit DispatcherConfig(const std::string name) : name(std::move(name)) { }
const std::string& GetName() const;
const std::map<identifier_t, std::string>& GetMappings() const;
void AddMapping(identifier_t identifier, const std::string& analyzer_name);
bool operator==(const DispatcherConfig& rhs) const;
bool operator!=(const DispatcherConfig& rhs) const;
private:
const std::string name;
std::map<identifier_t, std::string> mappings;
};
class Config {
public:
const std::vector<DispatcherConfig>& GetDispatchers() const;
std::optional<std::reference_wrapper<DispatcherConfig>> GetDispatcherConfig(const std::string& name);
DispatcherConfig& AddDispatcherConfig(const std::string& name);
void AddMapping(const std::string& name, identifier_t identifier, const std::string& analyzer_name);
private:
std::vector<DispatcherConfig> dispatchers;
};
}

11
src/llanalyzer/Defines.h Normal file
View file

@ -0,0 +1,11 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <cstdint>
namespace zeek::llanalyzer {
using identifier_t = uint32_t;
}

285
src/llanalyzer/Manager.cc Normal file
View file

@ -0,0 +1,285 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <list>
#include <pcap.h>
#include "Config.h"
#include "Manager.h"
#include "NetVar.h"
#include "ProtocolAnalyzerSet.h"
#include "plugin/Manager.h"
using namespace zeek::llanalyzer;
Manager::Manager()
: plugin::ComponentManager<llanalyzer::Tag, llanalyzer::Component>("LLAnalyzer", "Tag")
{
}
Manager::~Manager()
{
delete analyzer_set;
}
void Manager::InitPostScript()
{
auto llanalyzer_mapping = zeek::id::find("LLAnalyzer::config_map");
if ( ! llanalyzer_mapping )
return;
auto mapping_val = llanalyzer_mapping->GetVal()->AsVectorVal();
if ( mapping_val->Size() == 0 )
return;
Config configuration;
for (unsigned int i = 0; i < mapping_val->Size(); i++)
{
auto* rv = mapping_val->At(i)->AsRecordVal();
auto parent = rv->GetField("parent");
std::string parent_name = parent ? Lookup(parent->AsEnumVal())->Name() : "ROOT";
auto identifier = rv->GetField("identifier")->AsCount();
auto analyzer = rv->GetField("analyzer")->AsEnumVal();
configuration.AddMapping(parent_name, identifier, Lookup(analyzer)->Name());
}
analyzer_set = new ProtocolAnalyzerSet(configuration, "DefaultAnalyzer");
}
void Manager::Done()
{
}
void Manager::DumpDebug()
{
#ifdef DEBUG
DBG_LOG(DBG_LLANALYZER, "Available llanalyzers after zeek_init():");
for ( auto& current : GetComponents() )
{
DBG_LOG(DBG_LLANALYZER, " %s (%s)", current->Name().c_str(), IsEnabled(current->Tag()) ? "enabled" : "disabled");
}
// Dump Analyzer Set
if (analyzer_set)
analyzer_set->DumpDebug();
#endif
}
bool Manager::EnableAnalyzer(const Tag& tag)
{
Component* p = Lookup(tag);
if ( ! p )
return false;
DBG_LOG(DBG_LLANALYZER, "Enabling analyzer %s", p->Name().c_str());
p->SetEnabled(true);
return true;
}
bool Manager::EnableAnalyzer(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
DBG_LOG(DBG_LLANALYZER, "Enabling analyzer %s", p->Name().c_str());
p->SetEnabled(true);
return true;
}
bool Manager::DisableAnalyzer(const Tag& tag)
{
Component* p = Lookup(tag);
if ( ! p )
return false;
DBG_LOG(DBG_LLANALYZER, "Disabling analyzer %s", p->Name().c_str());
p->SetEnabled(false);
return true;
}
bool Manager::DisableAnalyzer(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
DBG_LOG(DBG_LLANALYZER, "Disabling analyzer %s", p->Name().c_str());
p->SetEnabled(false);
return true;
}
void Manager::DisableAllAnalyzers()
{
DBG_LOG(DBG_LLANALYZER, "Disabling all analyzers");
std::list<Component*> all_analyzers = GetComponents();
for ( const auto& analyzer : all_analyzers )
analyzer->SetEnabled(false);
}
zeek::llanalyzer::Tag Manager::GetAnalyzerTag(const char* name)
{
return GetComponentTag(name);
}
bool Manager::IsEnabled(Tag tag)
{
if ( ! tag )
return false;
Component* p = Lookup(tag);
if ( ! p )
return false;
return p->Enabled();
}
bool Manager::IsEnabled(EnumVal* val)
{
Component* p = Lookup(val);
if ( ! p )
return false;
return p->Enabled();
}
Analyzer* Manager::InstantiateAnalyzer(const Tag& tag)
{
Component* c = Lookup(tag);
if ( ! c )
{
reporter->InternalWarning("request to instantiate unknown llanalyzer");
return nullptr;
}
if ( ! c->Enabled() )
return nullptr;
if ( ! c->Factory() )
{
reporter->InternalWarning("analyzer %s cannot be instantiated dynamically", GetComponentName(tag).c_str());
return nullptr;
}
Analyzer* a = c->Factory()();
if ( ! a )
{
reporter->InternalWarning("analyzer instantiation failed");
return nullptr;
}
if ( tag != a->GetAnalyzerTag() )
{
reporter->InternalError("Mismatch of requested analyzer %s and instantiated analyzer %s. This usually means that the plugin author made a mistake.",
GetComponentName(tag).c_str(), GetComponentName(a->GetAnalyzerTag()).c_str());
return nullptr;
}
return a;
}
Analyzer* Manager::InstantiateAnalyzer(const std::string& name)
{
Tag tag = GetComponentTag(name);
return tag ? InstantiateAnalyzer(tag) : nullptr;
}
void Manager::ProcessPacket(Packet* packet)
{
#ifdef DEBUG
static size_t counter = 0;
DBG_LOG(DBG_LLANALYZER, "Analyzing packet %ld, ts=%.3f...", ++counter, packet->time);
#endif
if ( ! analyzer_set )
return;
// Dispatch and analyze layers
AnalyzerResult result = AnalyzerResult::Continue;
identifier_t next_layer_id = packet->link_type;
do
{
auto current_analyzer = analyzer_set->Dispatch(next_layer_id);
// Analyzer not found
if ( current_analyzer == nullptr )
{
DBG_LOG(DBG_LLANALYZER, "Could not find analyzer for identifier %#x", next_layer_id);
packet->Weird("no_suitable_analyzer_found");
break;
}
// Analyze this layer and get identifier of next layer protocol
std::tie(result, next_layer_id) = current_analyzer->Analyze(packet);
#ifdef DEBUG
switch ( result )
{
case AnalyzerResult::Continue:
DBG_LOG(DBG_LLANALYZER, "Analysis in %s succeeded, next layer identifier is %#x.",
current_analyzer->GetAnalyzerName(), next_layer_id);
break;
case AnalyzerResult::Terminate:
DBG_LOG(DBG_LLANALYZER, "Done, last found layer identifier was %#x.", next_layer_id);
break;
case AnalyzerResult::Failed:
DBG_LOG(DBG_LLANALYZER, "Analysis failed in %s", current_analyzer->GetAnalyzerName());
}
#endif
} while ( result == AnalyzerResult::Continue );
if ( result == AnalyzerResult::Terminate )
CustomEncapsulationSkip(packet);
// Processing finished, reset analyzer set state for next packet
analyzer_set->Reset();
}
void Manager::CustomEncapsulationSkip(Packet* packet)
{
if ( zeek::detail::encap_hdr_size > 0 )
{
auto pdata = packet->cur_pos;
// Blanket encapsulation. We assume that what remains is IP.
if ( pdata + zeek::detail::encap_hdr_size + sizeof(struct ip) >= packet->GetEndOfData() )
{
packet->Weird("no_ip_left_after_encap");
return;
}
pdata += zeek::detail::encap_hdr_size;
auto ip = (const struct ip*)pdata;
switch ( ip->ip_v )
{
case 4:
packet->l3_proto = L3_IPV4;
break;
case 6:
packet->l3_proto = L3_IPV6;
break;
default:
{
// Neither IPv4 nor IPv6.
packet->Weird("no_ip_in_encap");
return;
}
}
}
}

167
src/llanalyzer/Manager.h Normal file
View file

@ -0,0 +1,167 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <queue>
#include <vector>
#include "Tag.h"
#include "Analyzer.h"
#include "Component.h"
#include "AnalyzerSet.h"
#include "plugin/ComponentManager.h"
#include "iosource/Packet.h"
#include "../Dict.h"
#include "../net_util.h"
namespace zeek::llanalyzer {
class AnalyzerSet;
class Manager : public plugin::ComponentManager<Tag, Component> {
public:
/**
* Constructor.
*/
Manager();
/**
* Destructor.
*/
~Manager();
/**
* Second-stage initialization of the manager. This is called late
* during Bro's initialization after any scripts are processed.
*/
void InitPostScript();
/**
* Finished the manager's operations.
*/
void Done();
/**
* Dumps out the state of all registered analyzers to the \c analyzer
* debug stream. Should be called only after any \c zeek_init events
* have executed to ensure that any of their changes are applied.
*/
void DumpDebug(); // Called after zeek_init() events.
/**
* Enables an analyzer type. Only enabled analyzers will be
* instantiated for new connections.
*
* @param tag The analyzer's tag.
*
* @return True if successful.
*/
bool EnableAnalyzer(const Tag& tag);
/**
* Enables an analyzer type. Only enabled analyzers will be
* instantiated for new connections.
*
* @param tag The analyzer's tag as an enum of script type \c
* Analyzer::Tag.
*
* @return True if successful.
*/
bool EnableAnalyzer(EnumVal* tag);
/**
* Enables an analyzer type. Disabled analyzers will not be
* instantiated for new connections.
*
* @param tag The analyzer's tag.
*
* @return True if successful.
*/
bool DisableAnalyzer(const Tag& tag);
/**
* Disables an analyzer type. Disabled analyzers will not be
* instantiated for new connections.
*
* @param tag The analyzer's tag as an enum of script type \c
* Analyzer::Tag.
*
* @return True if successful.
*/
bool DisableAnalyzer(EnumVal* tag);
/**
* Disables all currently registered analyzers.
*/
void DisableAllAnalyzers();
/**
* Returns the tag associated with an analyer name, or the tag
* associated with an error if no such analyzer exists.
*
* @param name The canonical analyzer name to check.
*/
Tag GetAnalyzerTag(const char* name);
/**
* Returns true if an analyzer is enabled.
*
* @param tag The analyzer's tag.
*/
bool IsEnabled(Tag tag);
/**
* Returns true if an analyzer is enabled.
*
* @param tag The analyzer's tag as an enum of script type \c
* Analyzer::Tag.
*/
bool IsEnabled(EnumVal* tag);
/**
* Instantiates a new analyzer instance.
*
* @param tag The analyzer's tag.
*
* @return The new analyzer instance. Returns
* null if tag is invalid, the requested analyzer is disabled, or the
* analyzer can't be instantiated.
*/
Analyzer* InstantiateAnalyzer(const Tag& tag);
/**
* Instantiates a new analyzer.
*
* @param name The name of the analyzer.
*
* @return The new analyzer instance. Returns
* null if the name is not known or if the requested analyzer that is
* disabled.
*/
Analyzer* InstantiateAnalyzer(const std::string& name);
/**
* Processes a packet by applying the configured low layer analyzers.
*
* @param packet The packet to process.
*/
void ProcessPacket(Packet* packet);
protected:
/**
* Skips a fixed amount of packet data that is defined by encap_hdr_size.
* It is assumed that an IP header follows.
*
* @param packet The packet to adapt.
*/
void CustomEncapsulationSkip(Packet* packet);
private:
AnalyzerSet* analyzer_set = nullptr;
};
}
extern zeek::llanalyzer::Manager* llanalyzer_mgr;

View file

@ -0,0 +1,137 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "ProtocolAnalyzerSet.h"
namespace zeek::llanalyzer {
ProtocolAnalyzerSet::ProtocolAnalyzerSet(Config& configuration, const std::string& default_analyzer_name)
{
// Instantiate objects for all analyzers
for ( const auto& current_dispatcher_config : configuration.GetDispatchers() )
{
for ( const auto& current_mapping : current_dispatcher_config.GetMappings() )
{
// Check if already instantiated
if ( analyzers.count(current_mapping.second) != 0 )
continue;
// Check if analyzer exists
if ( Analyzer* newAnalyzer = llanalyzer_mgr->InstantiateAnalyzer(current_mapping.second) )
analyzers.emplace(current_mapping.second, newAnalyzer);
}
}
// Generate Dispatchers, starting at root
root_dispatcher = GetDispatcher(configuration, "ROOT");
if ( root_dispatcher == nullptr )
reporter->InternalError("No dispatching configuration for ROOT of llanalyzer set.");
// Set up default analysis
auto it = analyzers.find(default_analyzer_name);
if ( it != analyzers.end() )
default_analyzer = it->second;
else
default_analyzer = llanalyzer_mgr->InstantiateAnalyzer(default_analyzer_name);
default_dispatcher = nullptr;
if ( default_analyzer != nullptr )
default_dispatcher = GetDispatcher(configuration, default_analyzer_name);
current_state = root_dispatcher;
}
ProtocolAnalyzerSet::~ProtocolAnalyzerSet()
{
bool delete_default = default_analyzer != nullptr;
for ( const auto& current : analyzers )
{
if ( current.second == default_analyzer )
delete_default = false;
delete current.second;
}
if ( delete_default )
delete default_analyzer;
}
Analyzer* ProtocolAnalyzerSet::Dispatch(identifier_t identifier)
{
// Because leaf nodes (aka no more dispatching) can still have an existing analyzer that returns more identifiers,
// current_state needs to be checked to be not null. In this case there would have been an analyzer dispatched
// in the last layer, but no dispatcher for it (end of FSM)
const Value* result = nullptr;
if ( current_state )
result = current_state->Lookup(identifier);
if ( result == nullptr )
{
if ( current_state != default_dispatcher )
{
// Switch to default analysis once
current_state = default_dispatcher;
return default_analyzer;
}
return nullptr;
}
else
{
current_state = result->dispatcher;
return result->analyzer;
}
}
void ProtocolAnalyzerSet::Reset()
{
current_state = root_dispatcher;
}
void ProtocolAnalyzerSet::DumpDebug() const
{
#ifdef DEBUG
DBG_LOG(DBG_LLANALYZER, "ProtocolAnalyzerSet FSM:");
for ( const auto& current : dispatchers )
{
DBG_LOG(DBG_LLANALYZER, " Dispatcher (%p): %s", current.second, current.first.c_str());
current.second->DumpDebug();
}
#endif
}
Dispatcher* ProtocolAnalyzerSet::GetDispatcher(Config& configuration, const std::string& dispatcher_name)
{
// Is it already created?
if ( dispatchers.count(dispatcher_name) != 0 )
return dispatchers[dispatcher_name];
// Create new dispatcher from config
std::optional<std::reference_wrapper<DispatcherConfig>> dispatcher_config = configuration.GetDispatcherConfig(dispatcher_name);
if ( ! dispatcher_config )
// No such dispatcher found, this is therefore implicitly a leaf
return nullptr;
const auto& mappings = dispatcher_config->get().GetMappings();
Dispatcher* dispatcher = new dispatcher_impl();
dispatchers.emplace(dispatcher_name, dispatcher);
for ( const auto& current_mapping : mappings )
{
// No analyzer with this name. Report warning and ignore.
if ( analyzers.count(current_mapping.second) == 0 )
{
reporter->InternalWarning("No analyzer %s found for dispatching identifier %#x of %s, ignoring.",
current_mapping.second.c_str(),
current_mapping.first,
dispatcher_name.c_str());
continue;
}
dispatcher->Register(current_mapping.first, analyzers.at(current_mapping.second),
GetDispatcher(configuration, current_mapping.second));
}
return dispatcher;
}
}

View file

@ -0,0 +1,39 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "AnalyzerSet.h"
#include "Config.h"
#include "dispatchers/Dispatcher.h"
#include "dispatchers/UniversalDispatcher.h"
#include "dispatchers/VectorDispatcher.h"
namespace zeek::llanalyzer {
class ProtocolAnalyzerSet : public AnalyzerSet {
public:
explicit ProtocolAnalyzerSet(Config& configuration, const std::string& default_analyzer_name);
~ProtocolAnalyzerSet() override;
Analyzer* Dispatch(identifier_t identifier) override;
void Reset() override;
protected:
void DumpDebug() const override;
private:
using dispatcher_impl = VectorDispatcher;
//using dispatcher_impl = UniversalDispatcher;
std::map<std::string, Analyzer*> analyzers;
std::map<std::string, Dispatcher*> dispatchers;
Dispatcher* root_dispatcher = nullptr;
Dispatcher* default_dispatcher = nullptr;
Dispatcher* current_state = nullptr;
Analyzer* default_analyzer = nullptr;
Dispatcher* GetDispatcher(Config& configuration, const std::string& dispatcher_name);
};
}

41
src/llanalyzer/Tag.cc Normal file
View file

@ -0,0 +1,41 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Tag.h"
#include "Manager.h"
namespace zeek::llanalyzer {
Tag Tag::Error;
Tag::Tag(type_t type, subtype_t subtype)
: zeek::Tag(llanalyzer_mgr->GetTagType(), type, subtype)
{
}
Tag& Tag::operator=(const Tag& other)
{
zeek::Tag::operator=(other);
return *this;
}
const IntrusivePtr<EnumVal>& Tag::AsVal() const
{
return zeek::Tag::AsVal(llanalyzer_mgr->GetTagType());
}
EnumVal* Tag::AsEnumVal() const
{
return AsVal().get();
}
Tag::Tag(IntrusivePtr<EnumVal> val)
: zeek::Tag(std::move(val))
{
}
Tag::Tag(EnumVal* val)
: zeek::Tag({NewRef {}, val})
{
}
}

130
src/llanalyzer/Tag.h Normal file
View file

@ -0,0 +1,130 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include "zeek-config.h"
#include "../Tag.h"
namespace zeek::plugin {
template <class T> class TaggedComponent;
template <class T, class C> class ComponentManager;
}
namespace plugin {
template <class T>
using TaggedComponent [[deprecated("Remove in v4.1. Use zeek::plugin::TaggedComponent instead.")]] =
zeek::plugin::TaggedComponent<T>;
template <class T, class C>
using ComponentManager [[deprecated("Remove in v4.1. Use zeek::plugin::ComponentManager instead.")]] =
zeek::plugin::ComponentManager<T, C>;
}
namespace zeek::llanalyzer {
class Manager;
class Component;
/**
* Class to identify a protocol analyzer type.
*/
class Tag : public zeek::Tag {
public:
/*
* Copy constructor.
*/
Tag(const Tag& other) : zeek::Tag(other) { }
/**
* Default constructor. This initializes the tag with an error value
* that will make \c operator \c bool return false.
*/
Tag() : zeek::Tag() { }
/**
* Destructor.
*/
~Tag() = default;
/**
* Returns false if the tag represents an error value rather than a
* legal analyzer type.
*/
explicit operator bool() const { return *this != Tag(); }
/**
* Assignment operator.
*/
Tag& operator=(const Tag& other);
/**
* Compares two tags for equality.
*/
bool operator==(const Tag& other) const
{
return zeek::Tag::operator==(other);
}
/**
* Compares two tags for inequality.
*/
bool operator!=(const Tag& other) const
{
return zeek::Tag::operator!=(other);
}
/**
* Compares two tags for less-than relationship.
*/
bool operator<(const Tag& other) const
{
return zeek::Tag::operator<(other);
}
/**
* Returns the \c Analyzer::Tag enum that corresponds to this tag.
* The returned value does not have its ref-count increased.
*
* @param etype the script-layer enum type associated with the tag.
*/
const IntrusivePtr<EnumVal>& AsVal() const;
/**
* Returns the \c Analyzer::Tag enum that corresponds to this tag.
* The returned value does not have its ref-count increased.
*
* @param etype the script-layer enum type associated with the tag.
*/
[[deprecated("Remove in v4.1. Use AsVal() instead.")]]
EnumVal* AsEnumVal() const;
static Tag Error;
protected:
friend class llanalyzer::Manager;
friend class plugin::ComponentManager<Tag, Component>;
friend class plugin::TaggedComponent<Tag>;
/**
* Constructor.
*
* @param type The main type. Note that the \a llanalyzer::Manager
* manages the value space internally, so noone else should assign
* any main types.
*
* @param subtype The sub type, which is left to an analyzer for
* interpretation. By default it's set to zero.
*/
explicit Tag(type_t type, subtype_t subtype = 0);
/**
* Constructor.
*
* @param val An enum value of script type \c Analyzer::Tag.
*/
explicit Tag(IntrusivePtr<EnumVal> val);
[[deprecated("Remove in v4.1. Construct from IntrusivePtr instead")]]
explicit Tag(EnumVal* val);
};
}

View file

@ -0,0 +1,13 @@
include(ZeekSubdir)
include_directories(BEFORE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
set(dispatcher_SRCS
UniversalDispatcher.cc
VectorDispatcher.cc
)
bro_add_subdir_library(llanalyzer_dispatcher ${dispatcher_SRCS})

View file

@ -0,0 +1,47 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <map>
#include <utility>
#include "Analyzer.h"
#include "Defines.h"
namespace zeek::llanalyzer {
class Dispatcher; // Forward decl for Value
using register_pair = std::pair<identifier_t, std::pair<Analyzer*, Dispatcher*>>;
using register_map = std::map<identifier_t, std::pair<Analyzer*, Dispatcher*>>;
class Value {
public:
Analyzer* analyzer;
Dispatcher* dispatcher;
Value(Analyzer* analyzer, Dispatcher* dispatcher)
: analyzer(analyzer), dispatcher(dispatcher)
{
}
};
class Dispatcher {
public:
virtual ~Dispatcher() = default;
virtual bool Register(identifier_t identifier, Analyzer* analyzer, Dispatcher* dispatcher) = 0;
virtual void Register(const register_map& data)
{
for ( auto& current : data )
Register(current.first, current.second.first, current.second.second);
}
virtual const Value* Lookup(identifier_t identifier) const = 0;
virtual size_t Size() const = 0;
virtual void Clear() = 0;
virtual void DumpDebug() const = 0;
};
}

View file

@ -0,0 +1,210 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "UniversalDispatcher.h"
namespace zeek::llanalyzer {
UniversalDispatcher::UniversalDispatcher() : generator(rd())
{
SetBins(2);
table = std::vector<pair_t>(ONE << m, {0, nullptr});
// Initialize random engine
distribution_a = std::uniform_int_distribution<uint64_t>(1, ~static_cast<uint64_t>(0));
distribution_b = std::uniform_int_distribution<uint64_t>(0, (ONE << w_minus_m) - ONE);
// Initialize random parameters
RandomizeAB();
}
UniversalDispatcher::~UniversalDispatcher()
{
FreeValues();
}
bool UniversalDispatcher::Register(identifier_t identifier, Analyzer* analyzer, Dispatcher* dispatcher)
{
#if DEBUG > 1
std::shared_ptr<void> deferred(nullptr, [=](...) {
std::cout << "Inserted " << identifier << std::endl;
});
#endif
uint64_t hashed_id = Hash(identifier);
if ( table[hashed_id].second == nullptr )
{
// Free bin, insert the value
table[hashed_id] = std::make_pair(identifier, new Value(analyzer, dispatcher));
return true;
}
else if ( table[hashed_id].first != identifier )
{
// The bin is not empty, but the content isn't the to-be-inserted identifier --> resolve collision
// Create intermediate representation with the new element in it, then rehash with that data
std::vector<pair_t> intermediate = CreateIntermediate();
intermediate.emplace_back(identifier, new Value(analyzer, dispatcher));
// Try increasing the #bins until it works or it can't get any larger.
Rehash(intermediate);
return true;
}
// Analyzer with this ID is already registered.
return false;
}
void UniversalDispatcher::Register(const register_map& data)
{
// Analyzer already registered
for ( const auto& current : data )
{
if ( table[Hash(current.first)].second != nullptr )
throw std::invalid_argument("Analyzer " + std::to_string(current.first) + " already registered!");
}
// Create intermediate representation of current analyzer set, then add all new ones
std::vector<pair_t> intermediate = CreateIntermediate();
for ( const auto& current : data )
intermediate.emplace_back(current.first, new Value(current.second.first, current.second.second));
Rehash(intermediate);
}
Value* UniversalDispatcher::Lookup(identifier_t identifier) const
{
uint64_t hashed_id = Hash(identifier);
// The hashed_id can't be larger than the number of bins
assert(hashed_id < table.size() && "Hashed ID is outside of the hash table range!");
pair_t entry = table[hashed_id];
if ( entry.second != nullptr && entry.first == identifier )
return entry.second;
return nullptr;
}
size_t UniversalDispatcher::Size() const
{
size_t result = 0;
for ( const auto& current : table )
{
if ( current.second != nullptr )
result++;
}
return result;
}
void UniversalDispatcher::Clear()
{
// Free all analyzers
FreeValues();
SetBins(2);
table = std::vector<pair_t>(ONE << m, {0, nullptr});
RandomizeAB();
}
size_t UniversalDispatcher::BucketCount()
{
return table.size();
}
void UniversalDispatcher::Rehash()
{
// Intermediate representation is just the current table without nulls
Rehash(CreateIntermediate());
}
void UniversalDispatcher::DumpDebug() const
{
#ifdef DEBUG
DBG_LOG(DBG_LLANALYZER, " Dispatcher elements (used/total): %lu/%lu", Size(), table.size());
for ( size_t i = 0; i < table.size(); i++ )
{
if ( table[i].second != nullptr )
DBG_LOG(DBG_LLANALYZER, " %#8x => %s, %p", table[i].first, table[i].second->analyzer->GetAnalyzerName(), table[i].second->dispatcher);
}
#endif
}
// #######################
// ####### PRIVATE #######
// #######################
void UniversalDispatcher::FreeValues()
{
for ( auto& current : table )
{
delete current.second;
current.second = nullptr;
}
}
void UniversalDispatcher::Rehash(const std::vector<pair_t>& intermediate)
{
while ( ! FindCollisionFreeHashFunction(intermediate) )
{
DBG_LOG(DBG_LLANALYZER, "Rehashing did not work. Increasing #bins to %" PRIu64 " (%" PRIu64 " bit).", (uint64_t)std::pow(2, m + 1), m + 1);
SetBins(m + 1);
}
}
bool UniversalDispatcher::FindCollisionFreeHashFunction(const std::vector<pair_t>& intermediate)
{
// Don't even try if the number of values is larger than the number of buckets
if ( ONE << m < intermediate.size() )
return false;
// Remember the hash function parameters to not break the table if rehashing doesn't work
uint64_t stored_a = a;
uint64_t stored_b = b;
// Because the hash function hashes all values in the universe uniformly to m bins with probability 1/m
// we should at least try a multiple of #bins times.
for ( size_t i = 1; i <= (ONE << m); i++ )
{
// Step 1: Re-randomize hash function parameters
RandomizeAB();
// Step 2: Create new table
std::vector<pair_t> new_table(ONE << m, {0, nullptr});
// Step 3: Try to insert all elements into the new table with the new hash function
bool finished = true;
for ( const auto& current : intermediate )
{
uint64_t hashed_id = Hash(current.first);
assert(hashed_id < new_table.size());
if ( new_table[hashed_id].second == nullptr )
{
// Free bin, insert the value
new_table[hashed_id] = current;
}
else
{
// The bin is not empty which means there is a collision
// (there are no duplicates in the intermediate representation so that can't be the case)
finished = false;
break;
}
}
// Step 4: If the inserting finished without collisions, overwrite the previous table and exit
if ( finished )
{
DBG_LOG(DBG_LLANALYZER, "Took %lu rehash(es) to resolve.", i);
table = new_table;
return true;
}
}
// Finding a collision free hash function failed. Revert the hash function parameters.
a = stored_a;
b = stored_b;
return false;
}
}

View file

@ -0,0 +1,108 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <random>
#include "Dispatcher.h"
namespace zeek::llanalyzer {
class UniversalDispatcher : public Dispatcher {
public:
UniversalDispatcher();
~UniversalDispatcher() override;
bool Register(identifier_t identifier, Analyzer* analyzer, Dispatcher* dispatcher) override;
void Register(const register_map& data) override;
Value* Lookup(identifier_t identifier) const override;
size_t Size() const override;
void Clear() override;
void DumpDebug() const override;
size_t BucketCount();
// Rehashes the hash table including re-randomization of the hash function.
void Rehash();
private:
using pair_t = std::pair<identifier_t, Value*>;
static const uint64_t ONE = 1u;
// Chosen random constants for the currently selected collision free random hash function
uint64_t a = 0; // Needs to be a random odd positive value < 2^(sizeof(uint64_t) * 8)
uint64_t b = 0; // Needs to be a random non-negative value < 2^(((sizeof(uint64_t) * 8) - M)
// Current bits that define the number of bins. Initially 2 which means there are 2^2 = 4 bins.
uint64_t m = 2;
// Current shift value which is the number of bits that are "insignificant" because of the universe size.
uint64_t w_minus_m = 0;
// RNG
std::random_device rd;
std::mt19937_64 generator;
std::uniform_int_distribution<uint64_t> distribution_a;
std::uniform_int_distribution<uint64_t> distribution_b;
// Debug
#if DEBUG > 0
size_t nptr_counter = 0;
size_t mismatch_counter = 0;
size_t all_counter = 0;
#endif
std::vector<pair_t> table;
void FreeValues();
void Rehash(const std::vector<pair_t>& intermediate);
/**
* Tries to find a collision free hash function with the current number of buckets.
*
* @param intermediate The key-value set to store in the hashtable.
* @return true, iff it found a collision-free hash function.
*/
bool FindCollisionFreeHashFunction(const std::vector<pair_t>& intermediate);
[[nodiscard]] inline uint64_t Hash(const uint64_t value) const
{
return (a * value + b) >> w_minus_m;
}
inline void RandomizeAB()
{
do {
a = distribution_a(generator);
} while ( a % 2 == 0 );
b = distribution_b(generator);
}
inline void SetBins(uint64_t new_m)
{
if ( new_m > (sizeof(uint64_t) * 8) )
throw std::runtime_error("Number of bits for bin count too large.");
m = new_m;
w_minus_m = sizeof(uint64_t) * 8 - m;
distribution_b = std::uniform_int_distribution<uint64_t>(0, ((uint64_t)(1u) << w_minus_m) - (uint64_t)(1u));
}
inline std::vector<pair_t> CreateIntermediate()
{
std::vector<pair_t> intermediate;
for ( const auto& current : table )
{
if ( current.second != nullptr )
{
assert(current.second->analyzer != nullptr);
intermediate.emplace_back(current.first, current.second);
}
}
return intermediate;
}
};
}

View file

@ -0,0 +1,122 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <algorithm>
#include "VectorDispatcher.h"
namespace zeek::llanalyzer {
VectorDispatcher::~VectorDispatcher()
{
FreeValues();
}
bool VectorDispatcher::Register(identifier_t identifier, Analyzer* analyzer, Dispatcher* dispatcher)
{
// If the table has size 1 and the entry is nullptr, there was nothing added yet. Just add it.
if ( table.size() == 1 && table[0] == nullptr )
{
table[0] = new Value(analyzer, dispatcher);
lowest_identifier = identifier;
return true;
}
// If highestIdentifier == identifier, overwrite would happen -> no check needed, will return false
if ( GetHighestIdentifier() < identifier )
{
table.resize(table.size() + (identifier - GetHighestIdentifier()), nullptr);
}
else if ( identifier < lowest_identifier )
{
// Lower than the lowest registered identifier. Shift up by lowerBound - identifier
identifier_t distance = lowest_identifier - identifier;
table.resize(table.size() + distance, nullptr);
// Shift values
for ( ssize_t i = table.size() - 1; i >= 0; i-- )
{
if ( table[i] != nullptr )
{
table.at(i + distance) = table.at(i);
table.at(i) = nullptr;
}
}
lowest_identifier = identifier;
}
int64_t index = identifier - lowest_identifier;
if ( table[index] == nullptr )
{
table[index] = new Value(analyzer, dispatcher);
return true;
}
return false;
}
void VectorDispatcher::Register(const register_map& data)
{
// Search smallest and largest identifier and resize vector
const auto& lowest_new =
std::min_element(data.begin(), data.end(),
[](const register_pair& a, const register_pair& b) {
return a.first < b.first;
});
// Register lowest first in order to do shifting only once
Register(lowest_new->first, lowest_new->second.first, lowest_new->second.second);
for ( auto i = data.begin(); i != data.end(); i++ )
{
// Already added if i == lowest_new
if ( i == lowest_new )
continue;
if ( ! Register(i->first, i->second.first, i->second.second) )
throw std::invalid_argument("Analyzer already registered!");
}
}
const Value* VectorDispatcher::Lookup(identifier_t identifier) const
{
int64_t index = identifier - lowest_identifier;
if ( index >= 0 && index < static_cast<int64_t>(table.size()) && table[index] != nullptr )
return table[index];
return nullptr;
}
size_t VectorDispatcher::Size() const
{
return std::count_if(table.begin(), table.end(), [](const auto* v) { return v != nullptr; });
}
void VectorDispatcher::Clear()
{
FreeValues();
table.clear();
}
void VectorDispatcher::FreeValues()
{
for ( auto& current : table )
{
delete current;
current = nullptr;
}
}
void VectorDispatcher::DumpDebug() const
{
#ifdef DEBUG
DBG_LOG(DBG_LLANALYZER, " Dispatcher elements (used/total): %lu/%lu", Size(), table.size());
DBG_LOG(DBG_LLANALYZER, "TABLE SIZE %lu", table.size());
for ( size_t i = 0; i < table.size(); i++ )
{
if ( table[i] != nullptr )
DBG_LOG(DBG_LLANALYZER, " %#8lx => %s, %p", i+lowest_identifier, table[i]->analyzer->GetAnalyzerName(), table[i]->dispatcher);
}
#endif
}
}

View file

@ -0,0 +1,41 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <utility>
#include "Dispatcher.h"
namespace zeek::llanalyzer {
class VectorDispatcher : public Dispatcher {
public:
VectorDispatcher()
: table(std::vector<Value*>(1, nullptr))
{ }
~VectorDispatcher() override;
bool Register(identifier_t identifier, Analyzer* analyzer, Dispatcher* dispatcher) override;
void Register(const register_map& data) override;
const Value* Lookup(identifier_t identifier) const override;
size_t Size() const override;
void Clear() override;
protected:
void DumpDebug() const override;
private:
identifier_t lowest_identifier = 0;
std::vector<Value*> table;
void FreeValues();
inline identifier_t GetHighestIdentifier() const
{
return lowest_identifier + table.size() - 1;
}
};
}

View file

@ -0,0 +1,18 @@
add_subdirectory(default)
add_subdirectory(wrapper)
add_subdirectory(null)
add_subdirectory(ethernet)
add_subdirectory(vlan)
add_subdirectory(pppoe)
add_subdirectory(ppp_serial)
add_subdirectory(ieee802_11)
add_subdirectory(ieee802_11_radio)
add_subdirectory(fddi)
add_subdirectory(nflog)
add_subdirectory(mpls)
add_subdirectory(linux_sll)
add_subdirectory(arp)
add_subdirectory(ipv4)
add_subdirectory(ipv6)

View file

@ -0,0 +1,19 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "ARP.h"
using namespace zeek::llanalyzer::ARP;
ARPAnalyzer::ARPAnalyzer()
: zeek::llanalyzer::Analyzer("ARP")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> ARPAnalyzer::Analyze(Packet* packet)
{
// TODO: Make ARP analyzer a native LL analyzer
packet->l3_proto = L3_ARP;
// Leave LL analyzer land
return { AnalyzerResult::Terminate, 0 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::ARP {
class ARPAnalyzer : public Analyzer {
public:
ARPAnalyzer();
~ARPAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new ARPAnalyzer();
}
};
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE $ {CMAKE_CURRENT_SOURCE_DIR} $ {CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer ARP)
zeek_plugin_cc(ARP.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "ARP.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_ARP {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("ARP",
zeek::llanalyzer::ARP::ARPAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::ARP";
config.description = "ARP LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer Default)
zeek_plugin_cc(Default.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,28 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Default.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::Default;
DefaultAnalyzer::DefaultAnalyzer()
: zeek::llanalyzer::Analyzer("DefaultAnalyzer")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> DefaultAnalyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
// Assume we're pointing at IP. Just figure out which version.
if ( pdata + sizeof(struct ip) >= packet->GetEndOfData() )
{
packet->Weird("default_ll_analyser_failed");
return { AnalyzerResult::Failed, 0 };
}
auto ip = (const struct ip *)pdata;
identifier_t protocol = ip->ip_v;
return { AnalyzerResult::Continue, protocol };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::Default {
class DefaultAnalyzer : public Analyzer {
public:
DefaultAnalyzer();
~DefaultAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new DefaultAnalyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Default.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_Default {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("DefaultAnalyzer",
zeek::llanalyzer::Default::DefaultAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::DefaultAnalyzer";
config.description = "Default LL-Analyzer for IP fallback";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer Ethernet)
zeek_plugin_cc(Ethernet.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,75 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Ethernet.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::Ethernet;
EthernetAnalyzer::EthernetAnalyzer()
: zeek::llanalyzer::Analyzer("Ethernet")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> EthernetAnalyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
auto end_of_data = packet->GetEndOfData();
// Skip past Cisco FabricPath to encapsulated ethernet frame.
if ( pdata[12] == 0x89 && pdata[13] == 0x03 )
{
auto constexpr cfplen = 16;
if ( pdata + cfplen + 14 >= end_of_data )
{
packet->Weird("truncated_link_header_cfp");
return { AnalyzerResult::Failed, 0 };
}
pdata += cfplen;
}
// Get protocol being carried from the ethernet frame.
identifier_t protocol = (pdata[12] << 8) + pdata[13];
packet->eth_type = protocol;
packet->l2_dst = pdata;
packet->l2_src = pdata + 6;
// Ethernet II frames
if ( protocol >= 1536 )
{
pdata += 14;
return { AnalyzerResult::Continue, protocol };
}
// Other ethernet frame types
if ( protocol <= 1500 )
{
if ( pdata + 16 >= end_of_data )
{
packet->Weird("truncated_ethernet_frame");
return { AnalyzerResult::Failed, 0 };
}
// In the following we use undefined EtherTypes to signal uncommon
// frame types. This allows specialized analyzers to take over.
// Note that pdata remains at the start of the ethernet frame.
// IEEE 802.2 SNAP
if ( pdata[14] == 0xAA && pdata[15] == 0xAA)
return { AnalyzerResult::Continue, 1502 };
// Novell raw IEEE 802.3
if ( pdata[14] == 0xFF && pdata[15] == 0xFF)
return { AnalyzerResult::Continue, 1503 };
// IEEE 802.2 LLC
return { AnalyzerResult::Continue, 1501 };
}
// Undefined (1500 < EtherType < 1536)
packet->Weird("undefined_ether_type");
return { AnalyzerResult::Failed, protocol };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::Ethernet {
class EthernetAnalyzer : public Analyzer {
public:
EthernetAnalyzer();
~EthernetAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new EthernetAnalyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "Ethernet.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_Ethernet {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("Ethernet",
zeek::llanalyzer::Ethernet::EthernetAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::Ethernet";
config.description = "Ethernet LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer FDDI)
zeek_plugin_cc(FDDI.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,27 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "FDDI.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::FDDI;
FDDIAnalyzer::FDDIAnalyzer()
: zeek::llanalyzer::Analyzer("FDDI")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> FDDIAnalyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
auto hdr_size = 13 + 8; // FDDI header + LLC
if ( pdata + hdr_size >= packet->GetEndOfData() )
{
packet->Weird("FDDI_analyzer_failed");
return { AnalyzerResult::Failed, 0 };
}
// We just skip the header and hope for default analysis
pdata += hdr_size;
return { AnalyzerResult::Continue, -1 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::FDDI {
class FDDIAnalyzer : public zeek::llanalyzer::Analyzer {
public:
FDDIAnalyzer();
~FDDIAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static zeek::llanalyzer::Analyzer* Instantiate()
{
return new FDDIAnalyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "FDDI.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_FDDI {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("FDDI",
zeek::llanalyzer::FDDI::FDDIAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::FDDI";
config.description = "FDDI LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer IEEE802_11)
zeek_plugin_cc(IEEE802_11.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,113 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IEEE802_11.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::IEEE802_11;
IEEE802_11Analyzer::IEEE802_11Analyzer()
: zeek::llanalyzer::Analyzer("IEEE802_11")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> IEEE802_11Analyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
auto end_of_data = packet->GetEndOfData();
u_char len_80211 = 24; // minimal length of data frames
if ( pdata + len_80211 >= end_of_data )
{
packet->Weird("truncated_802_11_header");
return { AnalyzerResult::Failed, 0 };
}
u_char fc_80211 = pdata[0]; // Frame Control field
// Skip non-data frame types (management & control).
if ( ! ((fc_80211 >> 2) & 0x02) )
return { AnalyzerResult::Failed, 0 };
// Skip subtypes without data.
if ( (fc_80211 >> 4) & 0x04 )
return { AnalyzerResult::Failed, 0 };
// 'To DS' and 'From DS' flags set indicate use of the 4th
// address field.
if ( (pdata[1] & 0x03) == 0x03 )
len_80211 += packet->L2_ADDR_LEN;
// Look for the QoS indicator bit.
if ( (fc_80211 >> 4) & 0x08 )
{
// Skip in case of A-MSDU subframes indicated by QoS
// control field.
if ( pdata[len_80211] & 0x80 )
return { AnalyzerResult::Failed, 0 };
len_80211 += 2;
}
if ( pdata + len_80211 >= end_of_data )
{
packet->Weird("truncated_802_11_header");
return { AnalyzerResult::Failed, 0 };
}
// Determine link-layer addresses based
// on 'To DS' and 'From DS' flags
switch ( pdata[1] & 0x03 )
{
case 0x00:
packet->l2_src = pdata + 10;
packet->l2_dst = pdata + 4;
break;
case 0x01:
packet->l2_src = pdata + 10;
packet->l2_dst = pdata + 16;
break;
case 0x02:
packet->l2_src = pdata + 16;
packet->l2_dst = pdata + 4;
break;
case 0x03:
packet->l2_src = pdata + 24;
packet->l2_dst = pdata + 16;
break;
}
// skip 802.11 data header
pdata += len_80211;
if ( pdata + 8 >= end_of_data )
{
packet->Weird("truncated_802_11_header");
return { AnalyzerResult::Failed, 0 };
}
// Check that the DSAP and SSAP are both SNAP and that the control
// field indicates that this is an unnumbered frame.
// The organization code (24bits) needs to also be zero to
// indicate that this is encapsulated ethernet.
if ( pdata[0] == 0xAA && pdata[1] == 0xAA && pdata[2] == 0x03 &&
pdata[3] == 0 && pdata[4] == 0 && pdata[5] == 0 )
{
pdata += 6;
}
else
{
// If this is a logical link control frame without the
// possibility of having a protocol we care about, we'll
// just skip it for now.
return { AnalyzerResult::Failed, 0 };
}
identifier_t protocol = (pdata[0] << 8) + pdata[1];
pdata += 2;
return { AnalyzerResult::Continue, protocol };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::IEEE802_11 {
class IEEE802_11Analyzer : public Analyzer {
public:
IEEE802_11Analyzer();
~IEEE802_11Analyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new IEEE802_11Analyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IEEE802_11.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_IEEE802_11 {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("IEEE802_11",
zeek::llanalyzer::IEEE802_11::IEEE802_11Analyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::IEEE802_11";
config.description = "IEEE 802.11 LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer IEEE802_11_Radio)
zeek_plugin_cc(IEEE802_11_Radio.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,38 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include <pcap.h>
#include "IEEE802_11_Radio.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::IEEE802_11_Radio;
IEEE802_11_RadioAnalyzer::IEEE802_11_RadioAnalyzer()
: zeek::llanalyzer::Analyzer("IEEE802_11_Radio")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> IEEE802_11_RadioAnalyzer::Analyze(Packet* packet)
{
auto pdata = packet->cur_pos;
auto end_of_data = packet->GetEndOfData();
if ( pdata + 3 >= end_of_data )
{
packet->Weird("truncated_radiotap_header");
return { AnalyzerResult::Failed, 0 };
}
// Skip over the RadioTap header
int rtheader_len = (pdata[3] << 8) + pdata[2];
if ( pdata + rtheader_len >= end_of_data )
{
packet->Weird("truncated_radiotap_header");
return { AnalyzerResult::Failed, 0 };
}
packet->cur_pos += rtheader_len;
return { AnalyzerResult::Continue, DLT_IEEE802_11 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::IEEE802_11_Radio {
class IEEE802_11_RadioAnalyzer : public Analyzer {
public:
IEEE802_11_RadioAnalyzer();
~IEEE802_11_RadioAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new IEEE802_11_RadioAnalyzer();
}
};
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IEEE802_11_Radio.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_IEEE802_11_Radio {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("IEEE802_11_Radio",
zeek::llanalyzer::IEEE802_11_Radio::IEEE802_11_RadioAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::IEEE802_11_Radio";
config.description = "IEEE 802.11 Radiotap LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer IPv4)
zeek_plugin_cc(IPv4.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,18 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv4.h"
using namespace zeek::llanalyzer::IPv4;
IPv4Analyzer::IPv4Analyzer()
: zeek::llanalyzer::Analyzer("IPv4")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> IPv4Analyzer::Analyze(Packet* packet)
{
packet->l3_proto = L3_IPV4;
// Leave LL analyzer land
return { AnalyzerResult::Terminate, 0 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::IPv4 {
class IPv4Analyzer : public Analyzer {
public:
IPv4Analyzer();
~IPv4Analyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new IPv4Analyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv4.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_IPv4 {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("IPv4",
zeek::llanalyzer::IPv4::IPv4Analyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::IPv4";
config.description = "IPv4 LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE $ {CMAKE_CURRENT_SOURCE_DIR} $ {CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer IPv6)
zeek_plugin_cc(IPv6.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,18 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "IPv6.h"
using namespace zeek::llanalyzer::IPv6;
IPv6Analyzer::IPv6Analyzer()
: zeek::llanalyzer::Analyzer("IPv6")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> IPv6Analyzer::Analyze(Packet* packet)
{
packet->l3_proto = L3_IPV6;
// Leave LL analyzer land
return { AnalyzerResult::Terminate, 0 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::IPv6 {
class IPv6Analyzer : public Analyzer {
public:
IPv6Analyzer();
~IPv6Analyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new IPv6Analyzer();
}
};
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "IPv6.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_IPv6 {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("IPv6",
zeek::llanalyzer::IPv6::IPv6Analyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::IPv6";
config.description = "IPv6 LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE $ {CMAKE_CURRENT_SOURCE_DIR} $ {CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer LinuxSLL)
zeek_plugin_cc(LinuxSLL.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,34 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "LinuxSLL.h"
using namespace zeek::llanalyzer::LinuxSLL;
LinuxSLLAnalyzer::LinuxSLLAnalyzer()
: zeek::llanalyzer::Analyzer("LinuxSLL")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> LinuxSLLAnalyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
if ( pdata + sizeof(SLLHeader) >= packet->GetEndOfData() )
{
packet->Weird("truncated_Linux_SLL_header");
return { AnalyzerResult::Failed, 0 };
}
//TODO: Handle different ARPHRD_types
auto hdr = (const SLLHeader*)pdata;
identifier_t protocol = ntohs(hdr->protocol_type);
packet->l2_src = (u_char*) &(hdr->addr);
// SLL doesn't include a destination address in the header, but not setting l2_dst to something
// here will cause crashes elsewhere.
packet->l2_dst = Packet::L2_EMPTY_ADDR;
pdata += sizeof(SLLHeader);
return { AnalyzerResult::Continue, protocol };
}

View file

@ -0,0 +1,35 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::LinuxSLL {
class LinuxSLLAnalyzer : public Analyzer {
public:
LinuxSLLAnalyzer();
~LinuxSLLAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static Analyzer* Instantiate()
{
return new LinuxSLLAnalyzer();
}
private:
// Structure layout is based on https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
struct SLLHeader
{
uint16_t packet_type;
uint16_t arphrd_type;
uint16_t addr_len;
uint64_t addr;
uint16_t protocol_type;
} __attribute__((__packed__));
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "LinuxSLL.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_LinuxSLL {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("LinuxSLL",
zeek::llanalyzer::LinuxSLL::LinuxSLLAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::LinuxSLL";
config.description = "Linux cooked capture (SLL) LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer MPLS)
zeek_plugin_cc(MPLS.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,55 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "MPLS.h"
using namespace zeek::llanalyzer::MPLS;
MPLSAnalyzer::MPLSAnalyzer()
: zeek::llanalyzer::Analyzer("MPLS")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> MPLSAnalyzer::Analyze(Packet* packet)
{
auto& pdata = packet->cur_pos;
auto end_of_data = packet->GetEndOfData();
// Skip the MPLS label stack.
bool end_of_stack = false;
while ( ! end_of_stack )
{
if ( pdata + 4 >= end_of_data )
{
packet->Weird("truncated_link_header");
return { AnalyzerResult::Failed, 0 };
}
end_of_stack = *(pdata + 2u) & 0x01;
pdata += 4;
}
// According to RFC3032 the encapsulated protocol is not encoded.
// We assume that what remains is IP.
if ( pdata + sizeof(struct ip) >= end_of_data )
{
packet->Weird("no_ip_in_mpls_payload");
return { AnalyzerResult::Failed, 0 };
}
auto ip = (const struct ip*)pdata;
if ( ip->ip_v == 4 )
packet->l3_proto = L3_IPV4;
else if ( ip->ip_v == 6 )
packet->l3_proto = L3_IPV6;
else
{
// Neither IPv4 nor IPv6.
packet->Weird("no_ip_in_mpls_payload");
return { AnalyzerResult::Failed, 0 };
}
packet->hdr_size = (pdata - packet->data);
return { AnalyzerResult::Terminate, 0 };
}

View file

@ -0,0 +1,23 @@
// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <llanalyzer/Analyzer.h>
#include <llanalyzer/Component.h>
namespace zeek::llanalyzer::MPLS {
class MPLSAnalyzer : public zeek::llanalyzer::Analyzer {
public:
MPLSAnalyzer();
~MPLSAnalyzer() override = default;
std::tuple<AnalyzerResult, identifier_t> Analyze(Packet* packet) override;
static zeek::llanalyzer::Analyzer* Instantiate()
{
return new MPLSAnalyzer();
}
};
}

View file

@ -0,0 +1,24 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "MPLS.h"
#include "plugin/Plugin.h"
#include "llanalyzer/Component.h"
namespace zeek::plugin::LLAnalyzer_MPLS {
class Plugin : public zeek::plugin::Plugin {
public:
zeek::plugin::Configuration Configure()
{
AddComponent(new zeek::llanalyzer::Component("MPLS",
zeek::llanalyzer::MPLS::MPLSAnalyzer::Instantiate));
zeek::plugin::Configuration config;
config.name = "LLAnalyzer::MPLS";
config.description = "MPLS LL-Analyzer";
return config;
}
} plugin;
}

View file

@ -0,0 +1,8 @@
include(ZeekPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
zeek_plugin_begin(LLAnalyzer NFLog)
zeek_plugin_cc(NFLog.cc Plugin.cc)
zeek_plugin_end()

View file

@ -0,0 +1,80 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "NFLog.h"
#include "NetVar.h"
using namespace zeek::llanalyzer::NFLog;
NFLogAnalyzer::NFLogAnalyzer()
: zeek::llanalyzer::Analyzer("NFLog")
{
}
std::tuple<zeek::llanalyzer::AnalyzerResult, zeek::llanalyzer::identifier_t> NFLogAnalyzer::Analyze(Packet* packet) {
auto& pdata = packet->cur_pos;
auto end_of_data = packet->GetEndOfData();
// See https://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html
identifier_t protocol = pdata[0];
uint8_t version = pdata[1];
if ( version != 0 )
{
packet->Weird("unknown_nflog_version");
return { AnalyzerResult::Failed, 0 };
}
// Skip to TLVs.
pdata += 4;
uint16_t tlv_len;
uint16_t tlv_type;
while ( true )
{
if ( pdata + 4 >= end_of_data )
{
packet->Weird("nflog_no_pcap_payload");
return { AnalyzerResult::Failed, 0 };
}
// TLV Type and Length values are specified in host byte order
// (libpcap should have done any needed byteswapping already).
tlv_len = *(reinterpret_cast<const uint16_t*>(pdata));
tlv_type = *(reinterpret_cast<const uint16_t*>(pdata + 2));
auto constexpr nflog_type_payload = 9;
if ( tlv_type == nflog_type_payload )
{
// The raw packet payload follows this TLV.
pdata += 4;
break;
}
else
{
// The Length value includes the 4 octets for the Type and
// Length values, but TLVs are also implicitly padded to
// 32-bit alignments (that padding may not be included in
// the Length value).
if ( tlv_len < 4 )
{
packet->Weird("nflog_bad_tlv_len");
return { AnalyzerResult::Failed, 0 };
}
else
{
auto rem = tlv_len % 4;
if ( rem != 0 )
tlv_len += 4 - rem;
}
pdata += tlv_len;
}
}
return { AnalyzerResult::Continue, protocol };
}

Some files were not shown because too many files have changed in this diff Show more