mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 08:08:19 +00:00
Initial implementation of Lower-Level analyzers
This commit is contained in:
parent
f744d4c070
commit
b2e6c9ac9a
146 changed files with 3967 additions and 613 deletions
|
@ -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");
|
||||
|
@ -685,10 +181,30 @@ ValPtr Packet::FmtEUI48(const u_char* mac) const
|
|||
|
||||
void Packet::Describe(ODesc* d) const
|
||||
{
|
||||
const IP_Hdr ip = IP();
|
||||
d->Add(ip.SrcAddr());
|
||||
d->Add("->");
|
||||
d->Add(ip.DstAddr());
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue