mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Add handling for IPv6 extension header chains (addresses #531)
- The script-layer 'pkt_hdr' type is extended with a new 'ip6' field representing the full IPv6 header chain. - The 'new_packet' event is now raised for IPv6 packets (addresses #523) - A new event called 'ipv6_ext_header' is raised for any IPv6 packet containing extension headers. - A new event called 'esp_packet' is raised for any packets using ESP ('new_packet' and 'ipv6_ext_header' events provide connection info, but that info can't be provided here since the upper-layer payload is encrypted). - The 'unknown_protocol' weird is now raised more reliably when Bro sees a transport protocol or IPv6 extension header it can't handle. (addresses #522) Still need to do IPv6 fragment reassembly and needs more testing.
This commit is contained in:
parent
0639487aad
commit
eb9f686bb2
11 changed files with 724 additions and 110 deletions
|
@ -939,11 +939,154 @@ const IPPROTO_IGMP = 2; ##< Group management protocol.
|
||||||
const IPPROTO_IPIP = 4; ##< IP encapsulation in IP.
|
const IPPROTO_IPIP = 4; ##< IP encapsulation in IP.
|
||||||
const IPPROTO_TCP = 6; ##< TCP.
|
const IPPROTO_TCP = 6; ##< TCP.
|
||||||
const IPPROTO_UDP = 17; ##< User datagram protocol.
|
const IPPROTO_UDP = 17; ##< User datagram protocol.
|
||||||
|
const IPPROTO_IPV6 = 41; ##< IPv6 header.
|
||||||
const IPPROTO_RAW = 255; ##< Raw IP packet.
|
const IPPROTO_RAW = 255; ##< Raw IP packet.
|
||||||
|
|
||||||
## Values extracted from an IP header.
|
# Definitions for IPv6 extension headers.
|
||||||
|
const IPPROTO_HOPOPTS = 0; ##< IPv6 hop-by-hop-options header.
|
||||||
|
const IPPROTO_ROUTING = 43; ##< IPv6 routing header.
|
||||||
|
const IPPROTO_FRAGMENT = 44; ##< IPv6 fragment header.
|
||||||
|
const IPPROTO_ESP = 50; ##< IPv6 encapsulating security payload header.
|
||||||
|
const IPPROTO_AH = 51; ##< IPv6 authentication header.
|
||||||
|
const IPPROTO_NONE = 59; ##< IPv6 no next header.
|
||||||
|
const IPPROTO_DSTOPTS = 60; ##< IPv6 destination options header.
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 header.
|
||||||
##
|
##
|
||||||
## .. bro:see:: pkt_hdr discarder_check_ip
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr_chain ip6_hopopts ip6_dstopts ip6_routing
|
||||||
|
## ip6_fragment ip6_ah ip6_esp
|
||||||
|
type ip6_hdr: record {
|
||||||
|
class: count; ##< Traffic class.
|
||||||
|
flow: count; ##< Flow label.
|
||||||
|
len: count; ##< Payload length.
|
||||||
|
nxt: count; ##< Next header (RFC 1700 assigned number).
|
||||||
|
hlim: count; ##< Hop limit.
|
||||||
|
src: addr; ##< Source address.
|
||||||
|
dst: addr; ##< Destination address.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 extension header's (e.g. hop-by-hop or
|
||||||
|
## destination option headers) option field.
|
||||||
|
##
|
||||||
|
## .. bro:see:: ip6_hdr ip6_hdr_chain ip6_hopopts ip6_dstopts
|
||||||
|
type ip6_option: record {
|
||||||
|
otype: count; ##< Option type.
|
||||||
|
len: count; ##< Option data length.
|
||||||
|
data: string; ##< Option data.
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 Hop-by-Hop options extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain ip6_option
|
||||||
|
type ip6_hopopts: record {
|
||||||
|
## Next header (RFC 1700 assigned number).
|
||||||
|
nxt: count;
|
||||||
|
## Length of header in 8-octet units, excluding first unit.
|
||||||
|
len: count;
|
||||||
|
## The TLV encoded options;
|
||||||
|
options: vector of ip6_option;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 Destination options extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain ip6_option
|
||||||
|
type ip6_dstopts: record {
|
||||||
|
## Next header (RFC 1700 assigned number).
|
||||||
|
nxt: count;
|
||||||
|
## Length of header in 8-octet units, excluding first unit.
|
||||||
|
len: count;
|
||||||
|
## The TLV encoded options;
|
||||||
|
options: vector of ip6_option;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 Routing extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain
|
||||||
|
type ip6_routing: record {
|
||||||
|
## Next header (RFC 1700 assigned number).
|
||||||
|
nxt: count;
|
||||||
|
## Length of header in 8-octet units, excluding first unit.
|
||||||
|
len: count;
|
||||||
|
## Routing type.
|
||||||
|
rtype: count;
|
||||||
|
## Segments left.
|
||||||
|
segleft: count;
|
||||||
|
## Type-specific data.
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 Fragment extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain
|
||||||
|
type ip6_fragment: record {
|
||||||
|
## Next header (RFC 1700 assigned number).
|
||||||
|
nxt: count;
|
||||||
|
## 8-bit reserved field.
|
||||||
|
rsv1: count;
|
||||||
|
## Fragmentation offset.
|
||||||
|
offset: count;
|
||||||
|
## 2-bit reserved field.
|
||||||
|
rsv2: count;
|
||||||
|
## More fragments.
|
||||||
|
more: bool;
|
||||||
|
## Fragment identification.
|
||||||
|
id: count;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 Authentication extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain
|
||||||
|
type ip6_ah: record {
|
||||||
|
## Next header (RFC 1700 assigned number).
|
||||||
|
nxt: count;
|
||||||
|
## Length of header in 4-octet units, excluding first two units.
|
||||||
|
len: count;
|
||||||
|
## Reserved field.
|
||||||
|
rsv: count;
|
||||||
|
## Security Parameter Index.
|
||||||
|
spi: count;
|
||||||
|
## Sequence number.
|
||||||
|
seq: count;
|
||||||
|
## Authentication data.
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv6 ESP extension header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain
|
||||||
|
type ip6_esp: record {
|
||||||
|
## Security Parameters Index.
|
||||||
|
spi: count;
|
||||||
|
## Sequence number.
|
||||||
|
seq: count;
|
||||||
|
};
|
||||||
|
|
||||||
|
## An IPv6 header chain.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip_hdr
|
||||||
|
type ip6_hdr_chain: record {
|
||||||
|
## The main IPv6 header.
|
||||||
|
hdr: ip6_hdr;
|
||||||
|
## Hop-by-hop option extension header.
|
||||||
|
hopopts: vector of ip6_hopopts;
|
||||||
|
## Destination option extension headers.
|
||||||
|
dstopts: vector of ip6_dstopts;
|
||||||
|
## Routing extension headers.
|
||||||
|
routing: vector of ip6_routing;
|
||||||
|
## Fragment headers.
|
||||||
|
fragment: vector of ip6_fragment;
|
||||||
|
## Authentication extension headers.
|
||||||
|
ah: vector of ip6_ah;
|
||||||
|
## Encapsulating security payload headers.
|
||||||
|
esp: vector of ip6_esp;
|
||||||
|
|
||||||
|
## Order of extension headers identified by RFC 1700 assigned numbers.
|
||||||
|
ext_order: vector of count;
|
||||||
|
};
|
||||||
|
|
||||||
|
## Values extracted from an IPv4 header.
|
||||||
|
##
|
||||||
|
## .. bro:see:: pkt_hdr ip6_hdr discarder_check_ip
|
||||||
type ip_hdr: record {
|
type ip_hdr: record {
|
||||||
hl: count; ##< Header length in bytes.
|
hl: count; ##< Header length in bytes.
|
||||||
tos: count; ##< Type of service.
|
tos: count; ##< Type of service.
|
||||||
|
@ -1000,7 +1143,8 @@ type icmp_hdr: record {
|
||||||
##
|
##
|
||||||
## .. bro:see:: new_packet
|
## .. bro:see:: new_packet
|
||||||
type pkt_hdr: record {
|
type pkt_hdr: record {
|
||||||
ip: ip_hdr; ##< The IP header.
|
ip: ip_hdr &optional; ##< The IPv4 header if an IPv4 packet.
|
||||||
|
ip6: ip6_hdr_chain &optional; ##< The IPv6 header chain if an IPv6 packet.
|
||||||
tcp: tcp_hdr &optional; ##< The TCP header if a TCP packet.
|
tcp: tcp_hdr &optional; ##< The TCP header if a TCP packet.
|
||||||
udp: udp_hdr &optional; ##< The UDP header if a UDP packet.
|
udp: udp_hdr &optional; ##< The UDP header if a UDP packet.
|
||||||
icmp: icmp_hdr &optional; ##< The ICMP header if an ICMP packet.
|
icmp: icmp_hdr &optional; ##< The ICMP header if an ICMP packet.
|
||||||
|
|
|
@ -330,6 +330,7 @@ set(bro_SRCS
|
||||||
IntSet.cc
|
IntSet.cc
|
||||||
InterConn.cc
|
InterConn.cc
|
||||||
IOSource.cc
|
IOSource.cc
|
||||||
|
IP.cc
|
||||||
IPAddr.cc
|
IPAddr.cc
|
||||||
IRC.cc
|
IRC.cc
|
||||||
List.cc
|
List.cc
|
||||||
|
|
13
src/Frag.cc
13
src/Frag.cc
|
@ -27,7 +27,7 @@ void FragTimer::Dispatch(double t, int /* is_expire */)
|
||||||
|
|
||||||
FragReassembler::FragReassembler(NetSessions* arg_s,
|
FragReassembler::FragReassembler(NetSessions* arg_s,
|
||||||
const IP_Hdr* ip, const u_char* pkt,
|
const IP_Hdr* ip, const u_char* pkt,
|
||||||
uint32 frag_field, HashKey* k, double t)
|
HashKey* k, double t)
|
||||||
: Reassembler(0, ip->DstAddr(), REASSEM_IP)
|
: Reassembler(0, ip->DstAddr(), REASSEM_IP)
|
||||||
{
|
{
|
||||||
s = arg_s;
|
s = arg_s;
|
||||||
|
@ -41,7 +41,7 @@ FragReassembler::FragReassembler(NetSessions* arg_s,
|
||||||
reassembled_pkt = 0;
|
reassembled_pkt = 0;
|
||||||
frag_size = 0; // flag meaning "not known"
|
frag_size = 0; // flag meaning "not known"
|
||||||
|
|
||||||
AddFragment(t, ip, pkt, frag_field);
|
AddFragment(t, ip, pkt);
|
||||||
|
|
||||||
if ( frag_timeout != 0.0 )
|
if ( frag_timeout != 0.0 )
|
||||||
{
|
{
|
||||||
|
@ -60,8 +60,7 @@ FragReassembler::~FragReassembler()
|
||||||
delete key;
|
delete key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt,
|
void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt)
|
||||||
uint32 frag_field)
|
|
||||||
{
|
{
|
||||||
const struct ip* ip4 = ip->IP4_Hdr();
|
const struct ip* ip4 = ip->IP4_Hdr();
|
||||||
|
|
||||||
|
@ -72,16 +71,16 @@ void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt,
|
||||||
// attack.
|
// attack.
|
||||||
s->Weird("fragment_protocol_inconsistency", ip);
|
s->Weird("fragment_protocol_inconsistency", ip);
|
||||||
|
|
||||||
if ( frag_field & 0x4000 )
|
if ( ip->DF() )
|
||||||
// Linux MTU discovery for UDP can do this, for example.
|
// Linux MTU discovery for UDP can do this, for example.
|
||||||
s->Weird("fragment_with_DF", ip);
|
s->Weird("fragment_with_DF", ip);
|
||||||
|
|
||||||
int offset = (ntohs(ip4->ip_off) & 0x1fff) * 8;
|
int offset = ip->FragOffset();
|
||||||
int len = ntohs(ip4->ip_len);
|
int len = ntohs(ip4->ip_len);
|
||||||
int hdr_len = proto_hdr->ip_hl * 4;
|
int hdr_len = proto_hdr->ip_hl * 4;
|
||||||
int upper_seq = offset + len - hdr_len;
|
int upper_seq = offset + len - hdr_len;
|
||||||
|
|
||||||
if ( (frag_field & 0x2000) == 0 )
|
if ( ! ip->MF() )
|
||||||
{
|
{
|
||||||
// Last fragment.
|
// Last fragment.
|
||||||
if ( frag_size == 0 )
|
if ( frag_size == 0 )
|
||||||
|
|
|
@ -20,11 +20,10 @@ typedef void (FragReassembler::*frag_timer_func)(double t);
|
||||||
class FragReassembler : public Reassembler {
|
class FragReassembler : public Reassembler {
|
||||||
public:
|
public:
|
||||||
FragReassembler(NetSessions* s, const IP_Hdr* ip, const u_char* pkt,
|
FragReassembler(NetSessions* s, const IP_Hdr* ip, const u_char* pkt,
|
||||||
uint32 frag_field, HashKey* k, double t);
|
HashKey* k, double t);
|
||||||
~FragReassembler();
|
~FragReassembler();
|
||||||
|
|
||||||
void AddFragment(double t, const IP_Hdr* ip, const u_char* pkt,
|
void AddFragment(double t, const IP_Hdr* ip, const u_char* pkt);
|
||||||
uint32 frag_field);
|
|
||||||
|
|
||||||
void Expire(double t);
|
void Expire(double t);
|
||||||
void DeleteTimer();
|
void DeleteTimer();
|
||||||
|
|
273
src/IP.cc
Normal file
273
src/IP.cc
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#include "IP.h"
|
||||||
|
#include "Type.h"
|
||||||
|
#include "Val.h"
|
||||||
|
#include "Var.h"
|
||||||
|
|
||||||
|
static RecordType* ip_hdr_type = 0;
|
||||||
|
static RecordType* ip6_hdr_type = 0;
|
||||||
|
static RecordType* ip6_hdr_chain_type = 0;
|
||||||
|
static RecordType* ip6_option_type = 0;
|
||||||
|
static RecordType* ip6_hopopts_type = 0;
|
||||||
|
static RecordType* ip6_dstopts_type = 0;
|
||||||
|
static RecordType* ip6_routing_type = 0;
|
||||||
|
static RecordType* ip6_fragment_type = 0;
|
||||||
|
static RecordType* ip6_ah_type = 0;
|
||||||
|
static RecordType* ip6_esp_type = 0;
|
||||||
|
|
||||||
|
static inline RecordType* hdrType(RecordType*& type, const char* name)
|
||||||
|
{
|
||||||
|
if ( ! type ) type = internal_type(name)->AsRecordType();
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_Hdr::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_hdr_type, "ip6_hdr"));
|
||||||
|
const struct ip6_hdr* ip6 = (const struct ip6_hdr*)data;
|
||||||
|
rv->Assign(0, new Val(ntohl(ip6->ip6_flow) & 0x0ff00000, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(ntohl(ip6->ip6_flow) & 0x000fffff, TYPE_COUNT));
|
||||||
|
rv->Assign(2, new Val(ntohs(ip6->ip6_plen), TYPE_COUNT));
|
||||||
|
rv->Assign(3, new Val(ip6->ip6_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(4, new Val(ip6->ip6_hlim, TYPE_COUNT));
|
||||||
|
rv->Assign(5, new AddrVal(ip6->ip6_src));
|
||||||
|
rv->Assign(6, new AddrVal(ip6->ip6_dst));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VectorVal* BuildOptionsVal(const u_char* data, uint16 len)
|
||||||
|
{
|
||||||
|
VectorVal* vv = new VectorVal(new VectorType(ip6_option_type->Ref()));
|
||||||
|
while ( len > 0 )
|
||||||
|
{
|
||||||
|
const struct ip6_opt* opt = (const struct ip6_opt*) data;
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_option_type, "ip6_option"));
|
||||||
|
rv->Assign(0, new Val(opt->ip6o_type, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(opt->ip6o_len, TYPE_COUNT));
|
||||||
|
uint16 off = 2 * sizeof(uint8);
|
||||||
|
rv->Assign(2, new StringVal(
|
||||||
|
new BroString(data + off, opt->ip6o_len - off, 1)));
|
||||||
|
data += opt->ip6o_len + off;
|
||||||
|
len -= opt->ip6o_len + off;
|
||||||
|
vv->Assign(vv->Size(), rv, 0);
|
||||||
|
}
|
||||||
|
return vv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_HopOpts::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_hopopts_type, "ip6_hopopts"));
|
||||||
|
const struct ip6_hbh* hbh = (const struct ip6_hbh*)data;
|
||||||
|
rv->Assign(0, new Val(hbh->ip6h_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(hbh->ip6h_len, TYPE_COUNT));
|
||||||
|
uint16 off = 2 * sizeof(uint8);
|
||||||
|
rv->Assign(2, BuildOptionsVal(data + off, Length() - off));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_DstOpts::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_dstopts_type, "ip6_dstopts"));
|
||||||
|
const struct ip6_dest* dst = (const struct ip6_dest*)data;
|
||||||
|
rv->Assign(0, new Val(dst->ip6d_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(dst->ip6d_len, TYPE_COUNT));
|
||||||
|
uint16 off = 2 * sizeof(uint8);
|
||||||
|
rv->Assign(2, BuildOptionsVal(data + off, Length() - off));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_Routing::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_routing_type, "ip6_routing"));
|
||||||
|
const struct ip6_rthdr* rt = (const struct ip6_rthdr*)data;
|
||||||
|
rv->Assign(0, new Val(rt->ip6r_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(rt->ip6r_len, TYPE_COUNT));
|
||||||
|
rv->Assign(2, new Val(rt->ip6r_type, TYPE_COUNT));
|
||||||
|
rv->Assign(3, new Val(rt->ip6r_segleft, TYPE_COUNT));
|
||||||
|
uint16 off = 4 * sizeof(uint8);
|
||||||
|
rv->Assign(4, new StringVal(new BroString(data + off, Length() - off, 1)));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_Fragment::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_fragment_type, "ip6_fragment"));
|
||||||
|
const struct ip6_frag* frag = (const struct ip6_frag*)data;
|
||||||
|
rv->Assign(0, new Val(frag->ip6f_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(frag->ip6f_reserved, TYPE_COUNT));
|
||||||
|
rv->Assign(2, new Val(ntohs(frag->ip6f_offlg) & 0xfff8, TYPE_COUNT));
|
||||||
|
rv->Assign(3, new Val(ntohs(frag->ip6f_offlg) & 0x0006, TYPE_COUNT));
|
||||||
|
rv->Assign(4, new Val(ntohs(frag->ip6f_offlg) & 0x0001, TYPE_BOOL));
|
||||||
|
rv->Assign(5, new Val(ntohl(frag->ip6f_ident), TYPE_COUNT));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_AH::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_ah_type, "ip6_ah"));
|
||||||
|
rv->Assign(0, new Val(((ip6_ext*)data)->ip6e_nxt, TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(((ip6_ext*)data)->ip6e_len, TYPE_COUNT));
|
||||||
|
rv->Assign(2, new Val(ntohs(((uint16*)data)[1]), TYPE_COUNT));
|
||||||
|
rv->Assign(3, new Val(ntohl(((uint32*)data)[1]), TYPE_COUNT));
|
||||||
|
rv->Assign(4, new Val(ntohl(((uint32*)data)[2]), TYPE_COUNT));
|
||||||
|
uint16 off = 3 * sizeof(uint32);
|
||||||
|
rv->Assign(5, new StringVal(new BroString(data + off, Length() - off, 1)));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IPv6_ESP::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rv = new RecordVal(hdrType(ip6_esp_type, "ip6_esp"));
|
||||||
|
const uint32* esp = (const uint32*)data;
|
||||||
|
rv->Assign(0, new Val(ntohl(esp[0]), TYPE_COUNT));
|
||||||
|
rv->Assign(1, new Val(ntohl(esp[1]), TYPE_COUNT));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordVal* IP_Hdr::BuildRecordVal() const
|
||||||
|
{
|
||||||
|
RecordVal* rval = 0;
|
||||||
|
|
||||||
|
if ( ! ip_hdr_type )
|
||||||
|
{
|
||||||
|
ip_hdr_type = internal_type("ip_hdr")->AsRecordType();
|
||||||
|
ip6_hdr_type = internal_type("ip6_hdr")->AsRecordType();
|
||||||
|
ip6_hdr_chain_type = internal_type("ip6_hdr_chain")->AsRecordType();
|
||||||
|
ip6_hopopts_type = internal_type("ip6_hopopts")->AsRecordType();
|
||||||
|
ip6_dstopts_type = internal_type("ip6_dstopts")->AsRecordType();
|
||||||
|
ip6_routing_type = internal_type("ip6_routing")->AsRecordType();
|
||||||
|
ip6_fragment_type = internal_type("ip6_fragment")->AsRecordType();
|
||||||
|
ip6_ah_type = internal_type("ip6_ah")->AsRecordType();
|
||||||
|
ip6_esp_type = internal_type("ip6_esp")->AsRecordType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ip4 )
|
||||||
|
{
|
||||||
|
rval = new RecordVal(ip_hdr_type);
|
||||||
|
rval->Assign(0, new Val(ip4->ip_hl * 4, TYPE_COUNT));
|
||||||
|
rval->Assign(1, new Val(ip4->ip_tos, TYPE_COUNT));
|
||||||
|
rval->Assign(2, new Val(ntohs(ip4->ip_len), TYPE_COUNT));
|
||||||
|
rval->Assign(3, new Val(ntohs(ip4->ip_id), TYPE_COUNT));
|
||||||
|
rval->Assign(4, new Val(ip4->ip_ttl, TYPE_COUNT));
|
||||||
|
rval->Assign(5, new Val(ip4->ip_p, TYPE_COUNT));
|
||||||
|
rval->Assign(6, new AddrVal(ip4->ip_src.s_addr));
|
||||||
|
rval->Assign(7, new AddrVal(ip4->ip_dst.s_addr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rval = new RecordVal(ip6_hdr_chain_type);
|
||||||
|
|
||||||
|
VectorVal* hopopts = new VectorVal(new VectorType(ip6_hopopts_type->Ref()));
|
||||||
|
VectorVal* dstopts = new VectorVal(new VectorType(ip6_dstopts_type->Ref()));
|
||||||
|
VectorVal* routing = new VectorVal(new VectorType(ip6_routing_type->Ref()));
|
||||||
|
VectorVal* fragment = new VectorVal(new VectorType(ip6_fragment_type->Ref()));
|
||||||
|
VectorVal* ah = new VectorVal(new VectorType(ip6_ah_type->Ref()));
|
||||||
|
VectorVal* esp = new VectorVal(new VectorType(ip6_esp_type->Ref()));
|
||||||
|
VectorVal* order = new VectorVal(new VectorType(base_type(TYPE_COUNT)));
|
||||||
|
|
||||||
|
for ( size_t i = 1; i < ip6_hdrs->Size(); ++i )
|
||||||
|
{
|
||||||
|
RecordVal* v = ((*ip6_hdrs)[i])->BuildRecordVal();
|
||||||
|
uint8 type = ((*ip6_hdrs)[i])->Type();
|
||||||
|
switch (type) {
|
||||||
|
case IPPROTO_HOPOPTS:
|
||||||
|
hopopts->Assign(hopopts->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_ROUTING:
|
||||||
|
routing->Assign(routing->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_DSTOPTS:
|
||||||
|
dstopts->Assign(dstopts->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_FRAGMENT:
|
||||||
|
fragment->Assign(fragment->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_AH:
|
||||||
|
ah->Assign(ah->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_ESP:
|
||||||
|
esp->Assign(esp->Size(), v, 0);
|
||||||
|
break;
|
||||||
|
case IPPROTO_IPV6:
|
||||||
|
default:
|
||||||
|
reporter->InternalError("pkt_hdr assigned bad header %d", type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
order->Assign(i, new Val(type, TYPE_COUNT), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rval->Assign(0, ((*ip6_hdrs)[0])->BuildRecordVal());
|
||||||
|
rval->Assign(1, hopopts);
|
||||||
|
rval->Assign(2, dstopts);
|
||||||
|
rval->Assign(3, routing);
|
||||||
|
rval->Assign(4, fragment);
|
||||||
|
rval->Assign(5, ah);
|
||||||
|
rval->Assign(6, esp);
|
||||||
|
rval->Assign(7, order);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline IPv6_Hdr* getIPv6Header(uint8 type, const u_char* d)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case IPPROTO_IPV6:
|
||||||
|
return new IPv6_Hdr(d);
|
||||||
|
case IPPROTO_HOPOPTS:
|
||||||
|
return new IPv6_HopOpts(d);
|
||||||
|
case IPPROTO_ROUTING:
|
||||||
|
return new IPv6_Routing(d);
|
||||||
|
case IPPROTO_DSTOPTS:
|
||||||
|
return new IPv6_DstOpts(d);
|
||||||
|
case IPPROTO_FRAGMENT:
|
||||||
|
return new IPv6_Fragment(d);
|
||||||
|
case IPPROTO_AH:
|
||||||
|
return new IPv6_AH(d);
|
||||||
|
case IPPROTO_ESP:
|
||||||
|
return new IPv6_ESP(d);
|
||||||
|
default:
|
||||||
|
// should never get here if calls are protected by isIPv6ExtHeader()
|
||||||
|
reporter->InternalError("Unknown IPv6 header type: %d", type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// can't be reached
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool isIPv6ExtHeader(uint8 type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case IPPROTO_HOPOPTS:
|
||||||
|
case IPPROTO_ROUTING:
|
||||||
|
case IPPROTO_DSTOPTS:
|
||||||
|
case IPPROTO_FRAGMENT:
|
||||||
|
case IPPROTO_AH:
|
||||||
|
case IPPROTO_ESP:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPv6_Hdr_Chain::IPv6_Hdr_Chain(const struct ip6_hdr* ip6)
|
||||||
|
{
|
||||||
|
length = 0;
|
||||||
|
uint8 current_type, next_type;
|
||||||
|
next_type = IPPROTO_IPV6;
|
||||||
|
const u_char* hdrs = (const u_char*) ip6;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
current_type = next_type;
|
||||||
|
chain.push_back(getIPv6Header(current_type, hdrs));
|
||||||
|
next_type = chain[chain.size()-1]->NextHdr();
|
||||||
|
uint16 len = chain[chain.size()-1]->Length();
|
||||||
|
hdrs += len;
|
||||||
|
length += len;
|
||||||
|
} while ( current_type != IPPROTO_FRAGMENT &&
|
||||||
|
current_type != IPPROTO_ESP &&
|
||||||
|
isIPv6ExtHeader(next_type) );
|
||||||
|
}
|
196
src/IP.h
196
src/IP.h
|
@ -4,8 +4,139 @@
|
||||||
#define ip_h
|
#define ip_h
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "net_util.h"
|
||||||
#include "IPAddr.h"
|
#include "IPAddr.h"
|
||||||
#include <net_util.h>
|
#include "Reporter.h"
|
||||||
|
#include "Val.h"
|
||||||
|
#include "Type.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for IPv6 header/extensions.
|
||||||
|
*/
|
||||||
|
class IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_Hdr() : type(0), data(0) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the main IPv6 header.
|
||||||
|
*/
|
||||||
|
IPv6_Hdr(const u_char* d) : type(IPPROTO_IPV6), data(d) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an IPv6 header or extension header from assigned type number.
|
||||||
|
*/
|
||||||
|
IPv6_Hdr(uint8 t, const u_char* d) : type(t), data(d) {}
|
||||||
|
|
||||||
|
virtual ~IPv6_Hdr() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the assigned IPv6 extension header type number of the header
|
||||||
|
* that immediately follows this one.
|
||||||
|
*/
|
||||||
|
virtual uint8 NextHdr() const { return ((ip6_hdr*)data)->ip6_nxt; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the header in bytes.
|
||||||
|
*/
|
||||||
|
virtual uint16 Length() const { return 40; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the RFC 1700 assigned number indicating the header type.
|
||||||
|
*/
|
||||||
|
uint8 Type() const { return type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the script-layer record representation of the header.
|
||||||
|
*/
|
||||||
|
virtual RecordVal* BuildRecordVal() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8 type;
|
||||||
|
const u_char* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_HopOpts : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_HopOpts(const u_char* d) : IPv6_Hdr(IPPROTO_HOPOPTS, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_DstOpts : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_DstOpts(const u_char* d) : IPv6_Hdr(IPPROTO_DSTOPTS, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_Routing : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_Routing(const u_char* d) : IPv6_Hdr(IPPROTO_ROUTING, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_Fragment : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_Fragment(const u_char* d) : IPv6_Hdr(IPPROTO_FRAGMENT, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
uint16 Length() const { return 8; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_AH : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_AH(const u_char* d) : IPv6_Hdr(IPPROTO_AH, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
uint16 Length() const { return 8 + 4 * ((ip6_ext*)data)->ip6e_len; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_ESP : public IPv6_Hdr {
|
||||||
|
public:
|
||||||
|
IPv6_ESP(const u_char* d) : IPv6_Hdr(IPPROTO_ESP, d) {}
|
||||||
|
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
|
||||||
|
// encrypted payload begins after 8 bytes
|
||||||
|
uint16 Length() const { return 8; }
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPv6_Hdr_Chain {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Initializes the header chain from an IPv6 header structure.
|
||||||
|
*/
|
||||||
|
IPv6_Hdr_Chain(const struct ip6_hdr* ip6);
|
||||||
|
|
||||||
|
~IPv6_Hdr_Chain()
|
||||||
|
{ for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of headers in the chain.
|
||||||
|
*/
|
||||||
|
size_t Size() const { return chain.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of the length of all headers in the chain in bytes.
|
||||||
|
*/
|
||||||
|
uint16 TotalLength() const { return length; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the header at the given location in the chain.
|
||||||
|
*/
|
||||||
|
const IPv6_Hdr* operator[](const size_t i) const { return chain[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
vector<IPv6_Hdr*> chain;
|
||||||
|
uint16 length; // The summation of all header lengths in the chain in bytes.
|
||||||
|
};
|
||||||
|
|
||||||
class IP_Hdr {
|
class IP_Hdr {
|
||||||
public:
|
public:
|
||||||
|
@ -17,10 +148,12 @@ public:
|
||||||
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del)
|
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del)
|
||||||
: ip4(0), ip6(arg_ip6), del(arg_del)
|
: ip4(0), ip6(arg_ip6), del(arg_del)
|
||||||
{
|
{
|
||||||
|
ip6_hdrs = new IPv6_Hdr_Chain(ip6);
|
||||||
}
|
}
|
||||||
|
|
||||||
~IP_Hdr()
|
~IP_Hdr()
|
||||||
{
|
{
|
||||||
|
if ( ip6 ) delete ip6_hdrs;
|
||||||
if ( del )
|
if ( del )
|
||||||
{
|
{
|
||||||
if ( ip4 )
|
if ( ip4 )
|
||||||
|
@ -30,23 +163,23 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: audit usages of this for correct IPv6 support or IPv4 assumptions
|
||||||
const struct ip* IP4_Hdr() const { return ip4; }
|
const struct ip* IP4_Hdr() const { return ip4; }
|
||||||
|
|
||||||
const struct ip6_hdr* IP6_Hdr() const { return ip6; }
|
const struct ip6_hdr* IP6_Hdr() const { return ip6; }
|
||||||
|
|
||||||
IPAddr SrcAddr() const
|
IPAddr SrcAddr() const
|
||||||
{ return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); }
|
{ return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); }
|
||||||
|
|
||||||
IPAddr DstAddr() const
|
IPAddr DstAddr() const
|
||||||
{ return ip4 ? IPAddr(ip4->ip_dst) : IPAddr(ip6->ip6_dst); }
|
{ return ip4 ? IPAddr(ip4->ip_dst) : IPAddr(ip6->ip6_dst); }
|
||||||
|
|
||||||
//TODO: needs adapting/replacement for IPv6 support
|
|
||||||
uint16 ID4() const { return ip4 ? ip4->ip_id : 0; }
|
|
||||||
|
|
||||||
const u_char* Payload() const
|
const u_char* Payload() const
|
||||||
{
|
{
|
||||||
if ( ip4 )
|
if ( ip4 )
|
||||||
return ((const u_char*) ip4) + ip4->ip_hl * 4;
|
return ((const u_char*) ip4) + ip4->ip_hl * 4;
|
||||||
else
|
else
|
||||||
return ((const u_char*) ip6) + 40;
|
return ((const u_char*) ip6) + ip6_hdrs->TotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 PayloadLen() const
|
uint16 PayloadLen() const
|
||||||
|
@ -54,33 +187,60 @@ public:
|
||||||
if ( ip4 )
|
if ( ip4 )
|
||||||
return ntohs(ip4->ip_len) - ip4->ip_hl * 4;
|
return ntohs(ip4->ip_len) - ip4->ip_hl * 4;
|
||||||
else
|
else
|
||||||
return ntohs(ip6->ip6_plen);
|
return ntohs(ip6->ip6_plen) - ip6_hdrs->TotalLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 TotalLen() const
|
uint16 TotalLen() const
|
||||||
{
|
{ return ip4 ? ntohs(ip4->ip_len) : ntohs(ip6->ip6_plen) + 40; }
|
||||||
if ( ip4 )
|
|
||||||
return ntohs(ip4->ip_len);
|
uint16 HdrLen() const
|
||||||
else
|
{ return ip4 ? ip4->ip_hl * 4 : ip6_hdrs->TotalLength(); }
|
||||||
return ntohs(ip6->ip6_plen) + 40;
|
|
||||||
}
|
uint8 LastHeader() const
|
||||||
|
{ return ip4 ? IPPROTO_RAW :
|
||||||
|
((*ip6_hdrs)[ip6_hdrs->Size()-1])->Type(); }
|
||||||
|
|
||||||
uint16 HdrLen() const { return ip4 ? ip4->ip_hl * 4 : 40; }
|
|
||||||
unsigned char NextProto() const
|
unsigned char NextProto() const
|
||||||
{ return ip4 ? ip4->ip_p : ip6->ip6_nxt; }
|
{ return ip4 ? ip4->ip_p :
|
||||||
|
((*ip6_hdrs)[ip6_hdrs->Size()-1])->NextHdr(); }
|
||||||
|
|
||||||
unsigned char TTL() const
|
unsigned char TTL() const
|
||||||
{ return ip4 ? ip4->ip_ttl : ip6->ip6_hlim; }
|
{ return ip4 ? ip4->ip_ttl : ip6->ip6_hlim; }
|
||||||
|
|
||||||
|
//TODO: check for IPv6 Fragment ext. header
|
||||||
|
bool IsFragment() const
|
||||||
|
{ return ip4 ? (ntohs(ip4->ip_off) & 0x3fff) != 0 : false; }
|
||||||
|
|
||||||
|
//TODO: check for IPv6 Fragment ext. header
|
||||||
|
uint16 FragOffset() const
|
||||||
|
{ return ip4 ? (ntohs(ip4->ip_off) & 0x1fff) * 8 : 0; }
|
||||||
|
|
||||||
|
//TODO: check for IPv6 Fragment ext. header
|
||||||
uint16 FragField() const
|
uint16 FragField() const
|
||||||
{ return ntohs(ip4 ? ip4->ip_off : 0); }
|
{ return ip4 ? ntohs(ip4->ip_off) : 0; }
|
||||||
|
|
||||||
|
//TODO: check for IPv6 Fragment ext. header
|
||||||
|
uint16 ID() const
|
||||||
|
{ return ip4 ? ntohs(ip4->ip_id) : 0; }
|
||||||
|
|
||||||
|
//TODO: check for IPv6 Fragment ext. header
|
||||||
|
int MF() const
|
||||||
|
{ return ip4 ? (ntohs(ip4->ip_off) & 0x2000) != 0 : 0; }
|
||||||
|
|
||||||
|
// IPv6 has no "Don't Fragment" flag.
|
||||||
int DF() const
|
int DF() const
|
||||||
{ return ip4 ? ((ntohs(ip4->ip_off) & IP_DF) != 0) : 0; }
|
{ return ip4 ? ((ntohs(ip4->ip_off) & 0x4000) != 0) : 0; }
|
||||||
uint16 IP_ID() const
|
|
||||||
{ return ip4 ? (ntohs(ip4->ip_id)) : 0; }
|
size_t NumHeaders() const
|
||||||
|
{ return ip4 ? 1 : ip6_hdrs->Size(); }
|
||||||
|
|
||||||
|
RecordVal* BuildRecordVal() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const struct ip* ip4;
|
const struct ip* ip4;
|
||||||
const struct ip6_hdr* ip6;
|
const struct ip6_hdr* ip6;
|
||||||
bool del;
|
bool del;
|
||||||
|
IPv6_Hdr_Chain* ip6_hdrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -33,7 +33,7 @@ PacketSortElement::PacketSortElement(PktSrc* arg_src,
|
||||||
|
|
||||||
if ( ip_hdr->NextProto() == IPPROTO_TCP &&
|
if ( ip_hdr->NextProto() == IPPROTO_TCP &&
|
||||||
// Note: can't sort fragmented packets
|
// Note: can't sort fragmented packets
|
||||||
(ip_hdr->FragField() & 0x3fff) == 0 )
|
( ! ip_hdr->IsFragment() ) )
|
||||||
{
|
{
|
||||||
tcp_offset = hdr_size + ip_hdr->HdrLen();
|
tcp_offset = hdr_size + ip_hdr->HdrLen();
|
||||||
if ( caplen >= tcp_offset + sizeof(struct tcphdr) )
|
if ( caplen >= tcp_offset + sizeof(struct tcphdr) )
|
||||||
|
|
151
src/Sessions.cc
151
src/Sessions.cc
|
@ -332,7 +332,8 @@ void NetSessions::NextPacketSecondary(double /* t */, const struct pcap_pkthdr*
|
||||||
StringVal* cmd_val =
|
StringVal* cmd_val =
|
||||||
new StringVal(sp->Event()->Filter());
|
new StringVal(sp->Event()->Filter());
|
||||||
args->append(cmd_val);
|
args->append(cmd_val);
|
||||||
args->append(BuildHeader(ip));
|
IP_Hdr ip_hdr(ip, false);
|
||||||
|
args->append(BuildHeader(&ip_hdr));
|
||||||
// ### Need to queue event here.
|
// ### Need to queue event here.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -400,18 +401,6 @@ int NetSessions::CheckConnectionTag(Connection* conn)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool looks_like_IPv4_packet(int len, const struct ip* ip_hdr)
|
|
||||||
{
|
|
||||||
if ( (unsigned int) len < sizeof(struct ip) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ip_hdr->ip_v == 4 && ntohs(ip_hdr->ip_len) == len )
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
const IP_Hdr* ip_hdr, const u_char* const pkt,
|
||||||
int hdr_size)
|
int hdr_size)
|
||||||
|
@ -441,18 +430,9 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) )
|
if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int proto = ip_hdr->NextProto();
|
|
||||||
if ( proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
|
|
||||||
proto != IPPROTO_ICMP )
|
|
||||||
{
|
|
||||||
dump_this_packet = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FragReassembler* f = 0;
|
FragReassembler* f = 0;
|
||||||
uint32 frag_field = ip_hdr->FragField();
|
|
||||||
|
|
||||||
if ( (frag_field & 0x3fff) != 0 )
|
if ( ip_hdr->IsFragment() )
|
||||||
{
|
{
|
||||||
dump_this_packet = 1; // always record fragments
|
dump_this_packet = 1; // always record fragments
|
||||||
|
|
||||||
|
@ -463,12 +443,12 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
// Don't try to reassemble, that's doomed.
|
// Don't try to reassemble, that's doomed.
|
||||||
// Discard all except the first fragment (which
|
// Discard all except the first fragment (which
|
||||||
// is useful in analyzing header-only traces)
|
// is useful in analyzing header-only traces)
|
||||||
if ( (frag_field & 0x1fff) != 0 )
|
if ( ip_hdr->FragOffset() != 0 )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
f = NextFragment(t, ip_hdr, pkt + hdr_size, frag_field);
|
f = NextFragment(t, ip_hdr, pkt + hdr_size);
|
||||||
const IP_Hdr* ih = f->ReassembledPkt();
|
const IP_Hdr* ih = f->ReassembledPkt();
|
||||||
if ( ! ih )
|
if ( ! ih )
|
||||||
// It didn't reassemble into anything yet.
|
// It didn't reassemble into anything yet.
|
||||||
|
@ -485,21 +465,24 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
len -= ip_hdr_len; // remove IP header
|
len -= ip_hdr_len; // remove IP header
|
||||||
caplen -= ip_hdr_len;
|
caplen -= ip_hdr_len;
|
||||||
|
|
||||||
uint32 min_hdr_len = (proto == IPPROTO_TCP) ? sizeof(struct tcphdr) :
|
if ( ip_hdr->LastHeader() == IPPROTO_ESP )
|
||||||
(proto == IPPROTO_UDP ? sizeof(struct udphdr) : ICMP_MINLEN);
|
|
||||||
|
|
||||||
if ( len < min_hdr_len )
|
|
||||||
{
|
{
|
||||||
Weird("truncated_header", hdr, pkt);
|
if ( esp_packet )
|
||||||
if ( f )
|
{
|
||||||
Remove(f); // ###
|
val_list* vl = new val_list();
|
||||||
|
vl->append(ip_hdr->BuildRecordVal());
|
||||||
|
mgr.QueueEvent(esp_packet, vl);
|
||||||
|
}
|
||||||
|
Remove(f);
|
||||||
|
// Can't do more since upper-layer payloads are going to be encrypted
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( caplen < min_hdr_len )
|
|
||||||
|
int proto = ip_hdr->NextProto();
|
||||||
|
|
||||||
|
if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt) )
|
||||||
{
|
{
|
||||||
Weird("internally_truncated_header", hdr, pkt);
|
Remove(f);
|
||||||
if ( f )
|
|
||||||
Remove(f); // ###
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,6 +532,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Weird(fmt("unknown_protocol %d", proto), hdr, pkt);
|
Weird(fmt("unknown_protocol %d", proto), hdr, pkt);
|
||||||
|
Remove(f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,6 +558,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
if ( consistent < 0 )
|
if ( consistent < 0 )
|
||||||
{
|
{
|
||||||
delete h;
|
delete h;
|
||||||
|
Remove(f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,10 +577,11 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! conn )
|
if ( ! conn )
|
||||||
|
{
|
||||||
delete h;
|
delete h;
|
||||||
|
Remove(f);
|
||||||
if ( ! conn )
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int record_packet = 1; // whether to record the packet at all
|
int record_packet = 1; // whether to record the packet at all
|
||||||
int record_content = 1; // whether to record its data
|
int record_content = 1; // whether to record its data
|
||||||
|
@ -603,8 +589,17 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
int is_orig = (id.src_addr == conn->OrigAddr()) &&
|
int is_orig = (id.src_addr == conn->OrigAddr()) &&
|
||||||
(id.src_port == conn->OrigPort());
|
(id.src_port == conn->OrigPort());
|
||||||
|
|
||||||
if ( new_packet && ip4 )
|
Val* pkt_hdr_val = 0;
|
||||||
conn->Event(new_packet, 0, BuildHeader(ip4));
|
|
||||||
|
if ( ipv6_ext_headers && ip_hdr->NumHeaders() > 1 )
|
||||||
|
{
|
||||||
|
pkt_hdr_val = BuildHeader(ip_hdr);
|
||||||
|
conn->Event(new_packet, 0, pkt_hdr_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( new_packet )
|
||||||
|
conn->Event(new_packet, 0,
|
||||||
|
pkt_hdr_val ? pkt_hdr_val->Ref() : BuildHeader(ip_hdr));
|
||||||
|
|
||||||
conn->NextPacket(t, is_orig, ip_hdr, len, caplen, data,
|
conn->NextPacket(t, is_orig, ip_hdr, len, caplen, data,
|
||||||
record_packet, record_content,
|
record_packet, record_content,
|
||||||
|
@ -614,7 +609,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
{
|
{
|
||||||
// Above we already recorded the fragment in its entirety.
|
// Above we already recorded the fragment in its entirety.
|
||||||
f->DeleteTimer();
|
f->DeleteTimer();
|
||||||
Remove(f); // ###
|
Remove(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ( record_packet )
|
else if ( record_packet )
|
||||||
|
@ -630,10 +625,39 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Val* NetSessions::BuildHeader(const struct ip* ip)
|
bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
||||||
|
const struct pcap_pkthdr* h, const u_char* p)
|
||||||
|
{
|
||||||
|
uint32 min_hdr_len = 0;
|
||||||
|
switch ( proto ) {
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
min_hdr_len = sizeof(struct tcphdr);
|
||||||
|
break;
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
min_hdr_len = sizeof(struct udphdr);
|
||||||
|
break;
|
||||||
|
case IPPROTO_ICMP:
|
||||||
|
default:
|
||||||
|
min_hdr_len = ICMP_MINLEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( len < min_hdr_len )
|
||||||
|
{
|
||||||
|
Weird("truncated_header", h, p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ( caplen < min_hdr_len )
|
||||||
|
{
|
||||||
|
Weird("internally_truncated_header", h, p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Val* NetSessions::BuildHeader(const IP_Hdr* ip)
|
||||||
{
|
{
|
||||||
static RecordType* pkt_hdr_type = 0;
|
static RecordType* pkt_hdr_type = 0;
|
||||||
static RecordType* ip_hdr_type = 0;
|
|
||||||
static RecordType* tcp_hdr_type = 0;
|
static RecordType* tcp_hdr_type = 0;
|
||||||
static RecordType* udp_hdr_type = 0;
|
static RecordType* udp_hdr_type = 0;
|
||||||
static RecordType* icmp_hdr_type;
|
static RecordType* icmp_hdr_type;
|
||||||
|
@ -641,7 +665,6 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
if ( ! pkt_hdr_type )
|
if ( ! pkt_hdr_type )
|
||||||
{
|
{
|
||||||
pkt_hdr_type = internal_type("pkt_hdr")->AsRecordType();
|
pkt_hdr_type = internal_type("pkt_hdr")->AsRecordType();
|
||||||
ip_hdr_type = internal_type("ip_hdr")->AsRecordType();
|
|
||||||
tcp_hdr_type = internal_type("tcp_hdr")->AsRecordType();
|
tcp_hdr_type = internal_type("tcp_hdr")->AsRecordType();
|
||||||
udp_hdr_type = internal_type("udp_hdr")->AsRecordType();
|
udp_hdr_type = internal_type("udp_hdr")->AsRecordType();
|
||||||
icmp_hdr_type = internal_type("icmp_hdr")->AsRecordType();
|
icmp_hdr_type = internal_type("icmp_hdr")->AsRecordType();
|
||||||
|
@ -649,26 +672,15 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
|
|
||||||
RecordVal* pkt_hdr = new RecordVal(pkt_hdr_type);
|
RecordVal* pkt_hdr = new RecordVal(pkt_hdr_type);
|
||||||
|
|
||||||
RecordVal* ip_hdr = new RecordVal(ip_hdr_type);
|
if ( ip->IP4_Hdr() )
|
||||||
|
pkt_hdr->Assign(0, ip->BuildRecordVal());
|
||||||
int ip_hdr_len = ip->ip_hl * 4;
|
else
|
||||||
int ip_pkt_len = ntohs(ip->ip_len);
|
pkt_hdr->Assign(1, ip->BuildRecordVal());
|
||||||
|
|
||||||
ip_hdr->Assign(0, new Val(ip->ip_hl * 4, TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(1, new Val(ip->ip_tos, TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(2, new Val(ip_pkt_len, TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(3, new Val(ntohs(ip->ip_id), TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(4, new Val(ip->ip_ttl, TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(5, new Val(ip->ip_p, TYPE_COUNT));
|
|
||||||
ip_hdr->Assign(6, new AddrVal(ip->ip_src.s_addr));
|
|
||||||
ip_hdr->Assign(7, new AddrVal(ip->ip_dst.s_addr));
|
|
||||||
|
|
||||||
pkt_hdr->Assign(0, ip_hdr);
|
|
||||||
|
|
||||||
// L4 header.
|
// L4 header.
|
||||||
const u_char* data = ((const u_char*) ip) + ip_hdr_len;
|
const u_char* data = ip->Payload();
|
||||||
|
|
||||||
int proto = ip->ip_p;
|
int proto = ip->NextProto();
|
||||||
switch ( proto ) {
|
switch ( proto ) {
|
||||||
case IPPROTO_TCP:
|
case IPPROTO_TCP:
|
||||||
{
|
{
|
||||||
|
@ -676,7 +688,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
RecordVal* tcp_hdr = new RecordVal(tcp_hdr_type);
|
RecordVal* tcp_hdr = new RecordVal(tcp_hdr_type);
|
||||||
|
|
||||||
int tcp_hdr_len = tp->th_off * 4;
|
int tcp_hdr_len = tp->th_off * 4;
|
||||||
int data_len = ip_pkt_len - ip_hdr_len - tcp_hdr_len;
|
int data_len = ip->PayloadLen() - tcp_hdr_len;
|
||||||
|
|
||||||
tcp_hdr->Assign(0, new PortVal(ntohs(tp->th_sport), TRANSPORT_TCP));
|
tcp_hdr->Assign(0, new PortVal(ntohs(tp->th_sport), TRANSPORT_TCP));
|
||||||
tcp_hdr->Assign(1, new PortVal(ntohs(tp->th_dport), TRANSPORT_TCP));
|
tcp_hdr->Assign(1, new PortVal(ntohs(tp->th_dport), TRANSPORT_TCP));
|
||||||
|
@ -687,7 +699,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
tcp_hdr->Assign(6, new Val(tp->th_flags, TYPE_COUNT));
|
tcp_hdr->Assign(6, new Val(tp->th_flags, TYPE_COUNT));
|
||||||
tcp_hdr->Assign(7, new Val(ntohs(tp->th_win), TYPE_COUNT));
|
tcp_hdr->Assign(7, new Val(ntohs(tp->th_win), TYPE_COUNT));
|
||||||
|
|
||||||
pkt_hdr->Assign(1, tcp_hdr);
|
pkt_hdr->Assign(2, tcp_hdr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,7 +712,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
udp_hdr->Assign(1, new PortVal(ntohs(up->uh_dport), TRANSPORT_UDP));
|
udp_hdr->Assign(1, new PortVal(ntohs(up->uh_dport), TRANSPORT_UDP));
|
||||||
udp_hdr->Assign(2, new Val(ntohs(up->uh_ulen), TYPE_COUNT));
|
udp_hdr->Assign(2, new Val(ntohs(up->uh_ulen), TYPE_COUNT));
|
||||||
|
|
||||||
pkt_hdr->Assign(2, udp_hdr);
|
pkt_hdr->Assign(3, udp_hdr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +723,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
|
|
||||||
icmp_hdr->Assign(0, new Val(icmpp->icmp_type, TYPE_COUNT));
|
icmp_hdr->Assign(0, new Val(icmpp->icmp_type, TYPE_COUNT));
|
||||||
|
|
||||||
pkt_hdr->Assign(3, icmp_hdr);
|
pkt_hdr->Assign(4, icmp_hdr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,9 +737,9 @@ Val* NetSessions::BuildHeader(const struct ip* ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
|
FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
|
||||||
const u_char* pkt, uint32 frag_field)
|
const u_char* pkt)
|
||||||
{
|
{
|
||||||
uint32 frag_id = ntohs(ip->ID4()); // we actually could skip conv.
|
uint32 frag_id = ip->ID();
|
||||||
|
|
||||||
ListVal* key = new ListVal(TYPE_ANY);
|
ListVal* key = new ListVal(TYPE_ANY);
|
||||||
key->Append(new AddrVal(ip->SrcAddr()));
|
key->Append(new AddrVal(ip->SrcAddr()));
|
||||||
|
@ -741,7 +753,7 @@ FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
|
||||||
FragReassembler* f = fragments.Lookup(h);
|
FragReassembler* f = fragments.Lookup(h);
|
||||||
if ( ! f )
|
if ( ! f )
|
||||||
{
|
{
|
||||||
f = new FragReassembler(this, ip, pkt, frag_field, h, t);
|
f = new FragReassembler(this, ip, pkt, h, t);
|
||||||
fragments.Insert(h, f);
|
fragments.Insert(h, f);
|
||||||
Unref(key);
|
Unref(key);
|
||||||
return f;
|
return f;
|
||||||
|
@ -750,7 +762,7 @@ FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
|
||||||
delete h;
|
delete h;
|
||||||
Unref(key);
|
Unref(key);
|
||||||
|
|
||||||
f->AddFragment(t, ip, pkt, frag_field);
|
f->AddFragment(t, ip, pkt);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,6 +921,7 @@ void NetSessions::Remove(Connection* c)
|
||||||
|
|
||||||
void NetSessions::Remove(FragReassembler* f)
|
void NetSessions::Remove(FragReassembler* f)
|
||||||
{
|
{
|
||||||
|
if ( ! f ) return;
|
||||||
HashKey* k = f->Key();
|
HashKey* k = f->Key();
|
||||||
if ( ! k )
|
if ( ! k )
|
||||||
reporter->InternalError("fragment block not in dictionary");
|
reporter->InternalError("fragment block not in dictionary");
|
||||||
|
|
|
@ -79,7 +79,7 @@ public:
|
||||||
// Returns a reassembled packet, or nil if there are still
|
// Returns a reassembled packet, or nil if there are still
|
||||||
// some missing fragments.
|
// some missing fragments.
|
||||||
FragReassembler* NextFragment(double t, const IP_Hdr* ip,
|
FragReassembler* NextFragment(double t, const IP_Hdr* ip,
|
||||||
const u_char* pkt, uint32 frag_field);
|
const u_char* pkt);
|
||||||
|
|
||||||
int Get_OS_From_SYN(struct os_type* retval,
|
int Get_OS_From_SYN(struct os_type* retval,
|
||||||
uint16 tot, uint8 DF_flag, uint8 TTL, uint16 WSS,
|
uint16 tot, uint8 DF_flag, uint8 TTL, uint16 WSS,
|
||||||
|
@ -193,7 +193,13 @@ protected:
|
||||||
// Builds a record encapsulating a packet. This should be more
|
// Builds a record encapsulating a packet. This should be more
|
||||||
// general, including the equivalent of a union of tcp/udp/icmp
|
// general, including the equivalent of a union of tcp/udp/icmp
|
||||||
// headers .
|
// headers .
|
||||||
Val* BuildHeader(const struct ip* ip);
|
Val* BuildHeader(const IP_Hdr* ip);
|
||||||
|
|
||||||
|
// For a given protocol, checks whether the header's length as derived
|
||||||
|
// from lower-level headers or the length actually captured is less
|
||||||
|
// than that protocol's minimum header size.
|
||||||
|
bool CheckHeaderTrunc(int proto, uint32 len, uint32 caplen,
|
||||||
|
const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||||
|
|
||||||
CompositeHash* ch;
|
CompositeHash* ch;
|
||||||
PDict(Connection) tcp_conns;
|
PDict(Connection) tcp_conns;
|
||||||
|
|
|
@ -1203,7 +1203,7 @@ RecordVal* TCP_Analyzer::BuildOSVal(int is_orig, const IP_Hdr* ip,
|
||||||
if ( ip->HdrLen() > 20 )
|
if ( ip->HdrLen() > 20 )
|
||||||
quirks |= QUIRK_IPOPT;
|
quirks |= QUIRK_IPOPT;
|
||||||
|
|
||||||
if ( ip->IP_ID() == 0 )
|
if ( ip->ID() == 0 )
|
||||||
quirks |= QUIRK_ZEROID;
|
quirks |= QUIRK_ZEROID;
|
||||||
|
|
||||||
if ( tcp->th_seq == 0 )
|
if ( tcp->th_seq == 0 )
|
||||||
|
@ -1942,11 +1942,11 @@ int TCPStats_Endpoint::DataSent(double /* t */, int seq, int len, int caplen,
|
||||||
{
|
{
|
||||||
if ( ++num_pkts == 1 )
|
if ( ++num_pkts == 1 )
|
||||||
{ // First packet.
|
{ // First packet.
|
||||||
last_id = ntohs(ip->ID4());
|
last_id = ip->ID();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = ntohs(ip->ID4());
|
int id = ip->ID();
|
||||||
|
|
||||||
if ( id == last_id )
|
if ( id == last_id )
|
||||||
{
|
{
|
||||||
|
|
|
@ -454,11 +454,30 @@ event expected_connection_seen%(c: connection, a: count%);
|
||||||
##
|
##
|
||||||
## c: The connection the packet is part of.
|
## c: The connection the packet is part of.
|
||||||
##
|
##
|
||||||
## p: Informattion from the header of the packet that triggered the event.
|
## p: Information from the header of the packet that triggered the event.
|
||||||
##
|
##
|
||||||
## .. bro:see:: tcp_packet packet_contents
|
## .. bro:see:: tcp_packet packet_contents
|
||||||
event new_packet%(c: connection, p: pkt_hdr%);
|
event new_packet%(c: connection, p: pkt_hdr%);
|
||||||
|
|
||||||
|
## Generated for every IPv6 packet that contains extension headers.
|
||||||
|
## This is potentially an expensive event to handle if analysiing IPv6 traffic
|
||||||
|
## that happens to utilize extension headers frequently.
|
||||||
|
##
|
||||||
|
## c: The connection the packet is part of.
|
||||||
|
##
|
||||||
|
## p: Information from the header of the packet that triggered the event.
|
||||||
|
##
|
||||||
|
## .. bro:see:: new_packet tcp_packet packet_contents esp_packet
|
||||||
|
event ipv6_ext_headers%(c: connection, p: pkt_hdr%);
|
||||||
|
|
||||||
|
## Generated for any packets using the IPv6 Encapsulating Security Payload (ESP)
|
||||||
|
## extension header.
|
||||||
|
##
|
||||||
|
## p: Information from the header of the packet that triggered the event.
|
||||||
|
##
|
||||||
|
## .. bro:see:: new_packet tcp_packet ipv6_ext_headers
|
||||||
|
event esp_packet%(p: pkt_hdr%);
|
||||||
|
|
||||||
## Generated for every packet that has non-empty transport-layer payload. This is a
|
## Generated for every packet that has non-empty transport-layer payload. This is a
|
||||||
## very low-level and expensive event that should be avoided when at all possible.
|
## very low-level and expensive event that should be avoided when at all possible.
|
||||||
## It's usually infeasible to handle when processing even medium volumes of
|
## It's usually infeasible to handle when processing even medium volumes of
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue