Fixes for IPv6 truncation and ICMP/ICMP6 analysis.

- Add more guards against trying to analyze captured packets with a
  truncated IPv6 static header or extension header chain.

- Add back in the ICMP payload tracking for ICMP "connections".

- Fix 'icmp_context' record construction.  Some field assignments
  were mismatched for ICMP and ICMP6.  Source and destination
  addresses were set incorrectly for context packets that don't
  contain a full IP header.  Some fields for ICMP6 weren't filled out.

- Changed ICMP Time Exceeded packets to raise the 'icmp_time_exceeded'
  event instead of 'icmp_error_message'.

- Add unit tests for truncation and the main types of ICMP/ICMP6
  that have specific events.

- Documentation clarifications.
This commit is contained in:
Jon Siwek 2012-04-11 16:27:31 -05:00
parent 27ba3118c1
commit 51bad73e1e
39 changed files with 422 additions and 52 deletions

View file

@ -150,7 +150,7 @@ void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt)
void FragReassembler::Overlap(const u_char* b1, const u_char* b2, int n)
{
IP_Hdr proto_h(proto_hdr, false);
IP_Hdr proto_h(proto_hdr, false, proto_hdr_len);
if ( memcmp((const void*) b1, (const void*) b2, n) )
s->Weird("fragment_inconsistency", &proto_h);
@ -182,7 +182,7 @@ void FragReassembler::BlockInserted(DataBlock* /* start_block */)
// can happen for benign reasons when we're
// intermingling parts of two fragmented packets.
IP_Hdr proto_h(proto_hdr, false);
IP_Hdr proto_h(proto_hdr, false, proto_hdr_len);
s->Weird("fragment_size_inconsistency", &proto_h);
// We decide to analyze the contiguous portion now.
@ -196,7 +196,7 @@ void FragReassembler::BlockInserted(DataBlock* /* start_block */)
else if ( last_block->upper > frag_size )
{
IP_Hdr proto_h(proto_hdr, false);
IP_Hdr proto_h(proto_hdr, false, proto_hdr_len);
s->Weird("fragment_size_inconsistency", &proto_h);
frag_size = last_block->upper;
}
@ -250,8 +250,8 @@ void FragReassembler::BlockInserted(DataBlock* /* start_block */)
{
struct ip6_hdr* reassem6 = (struct ip6_hdr*) pkt_start;
reassem6->ip6_plen = htons(frag_size + proto_hdr_len - 40);
const IPv6_Hdr_Chain* chain = new IPv6_Hdr_Chain(reassem6, next_proto);
reassembled_pkt = new IP_Hdr(reassem6, true, chain);
const IPv6_Hdr_Chain* chain = new IPv6_Hdr_Chain(reassem6, next_proto, n);
reassembled_pkt = new IP_Hdr(reassem6, true, n, chain);
}
else

View file

@ -93,6 +93,12 @@ void ICMP_Analyzer::DeliverPacket(int len, const u_char* data,
caplen -= 8;
len -= 8;
int& len_stat = is_orig ? request_len : reply_len;
if ( len_stat < 0 )
len_stat = len;
else
len_stat += len;
if ( ip->NextProto() == IPPROTO_ICMP )
NextICMP4(current_timestamp, icmpp, len, caplen, data, ip);
else
@ -286,13 +292,12 @@ RecordVal* ICMP_Analyzer::ExtractICMP4Context(int len, const u_char*& data)
IPAddr src_addr, dst_addr;
uint32 src_port, dst_port;
if ( ip_hdr_len < sizeof(struct ip) || ip_hdr_len > uint32(len) )
if ( len < (int)sizeof(struct ip) || ip_hdr_len > uint32(len) )
{
// We don't have an entire IP header.
bad_hdr_len = 1;
ip_len = frag_offset = 0;
DF = MF = bad_checksum = 0;
src_addr = dst_addr = 0;
src_port = dst_port = 0;
}
@ -331,9 +336,9 @@ RecordVal* ICMP_Analyzer::ExtractICMP4Context(int len, const u_char*& data)
iprec->Assign(0, id_val);
iprec->Assign(1, new Val(ip_len, TYPE_COUNT));
iprec->Assign(2, new Val(proto, TYPE_COUNT));
iprec->Assign(3, new Val(bad_hdr_len, TYPE_BOOL));
iprec->Assign(4, new Val(bad_checksum, TYPE_BOOL));
iprec->Assign(5, new Val(frag_offset, TYPE_COUNT));
iprec->Assign(3, new Val(frag_offset, TYPE_COUNT));
iprec->Assign(4, new Val(bad_hdr_len, TYPE_BOOL));
iprec->Assign(5, new Val(bad_checksum, TYPE_BOOL));
iprec->Assign(6, new Val(MF, TYPE_BOOL));
iprec->Assign(7, new Val(DF, TYPE_BOOL));
@ -342,32 +347,33 @@ RecordVal* ICMP_Analyzer::ExtractICMP4Context(int len, const u_char*& data)
RecordVal* ICMP_Analyzer::ExtractICMP6Context(int len, const u_char*& data)
{
const IP_Hdr ip_hdr_data((const struct ip6_hdr*) data, false);
const IP_Hdr* ip_hdr = &ip_hdr_data;
int DF = 0, MF = 0, bad_hdr_len = 0, bad_checksum = 0;
int DF = 0, MF = 0, bad_hdr_len = 0;
TransportProto proto = TRANSPORT_UNKNOWN;
uint32 ip_hdr_len = ip_hdr->HdrLen(); //should always be 40
IPAddr src_addr;
IPAddr dst_addr;
uint32 ip_len, frag_offset = 0;
uint32 src_port, dst_port;
if ( ip_hdr_len < sizeof(struct ip6_hdr) || ip_hdr_len != 40 ) // XXX What's the 2nd part doing?
if ( len < (int)sizeof(struct ip6_hdr) )
{
bad_hdr_len = 1;
ip_len = 0;
src_addr = dst_addr = 0;
src_port = dst_port = 0;
}
else
{
ip_len = ip_hdr->TotalLen();
const IP_Hdr ip_hdr_data((const struct ip6_hdr*) data, false, len);
const IP_Hdr* ip_hdr = &ip_hdr_data;
ip_len = ip_hdr->TotalLen();
src_addr = ip_hdr->SrcAddr();
dst_addr = ip_hdr->DstAddr();
frag_offset = ip_hdr->FragOffset();
MF = ip_hdr->MF();
DF = ip_hdr->DF();
if ( uint32(len) >= ip_hdr_len + 4 )
if ( uint32(len) >= uint32(ip_hdr->HdrLen() + 4) )
proto = GetContextProtocol(ip_hdr, &src_port, &dst_port);
else
{
@ -388,17 +394,13 @@ RecordVal* ICMP_Analyzer::ExtractICMP6Context(int len, const u_char*& data)
iprec->Assign(0, id_val);
iprec->Assign(1, new Val(ip_len, TYPE_COUNT));
//if the encap packet is ICMPv6 we force this... (cause there is no IGMP (by that name) for ICMPv6), rather ugly hack once more
iprec->Assign(2, new Val(58, TYPE_COUNT));
iprec->Assign(3, new Val(bad_hdr_len, TYPE_BOOL));
// The following are not available for IPv6.
iprec->Assign(4, new Val(0, TYPE_BOOL)); // bad_checksum
iprec->Assign(5, new Val(frag_offset, TYPE_COUNT)); // frag_offset
iprec->Assign(6, new Val(0, TYPE_BOOL)); // MF
iprec->Assign(7, new Val(1, TYPE_BOOL)); // DF
iprec->Assign(2, new Val(proto, TYPE_COUNT));
iprec->Assign(3, new Val(frag_offset, TYPE_COUNT));
iprec->Assign(4, new Val(bad_hdr_len, TYPE_BOOL));
// bad_checksum is always false since IPv6 layer doesn't have a checksum
iprec->Assign(5, new Val(0, TYPE_BOOL));
iprec->Assign(6, new Val(MF, TYPE_BOOL));
iprec->Assign(7, new Val(DF, TYPE_BOOL));
return iprec;
}
@ -608,7 +610,7 @@ void ICMP_Analyzer::Context4(double t, const struct icmp* icmpp,
break;
case ICMP_TIMXCEED:
f = icmp_error_message;
f = icmp_time_exceeded;
break;
}

View file

@ -419,20 +419,35 @@ static inline bool isIPv6ExtHeader(uint8 type)
}
}
void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, bool set_next, uint16 next)
void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, int total_len,
bool set_next, uint16 next)
{
length = 0;
uint8 current_type, next_type;
next_type = IPPROTO_IPV6;
const u_char* hdrs = (const u_char*) ip6;
if ( total_len < (int)sizeof(struct ip6_hdr) )
reporter->InternalError("IPv6_HdrChain::Init with truncated IP header");
do
{
// We can't determine a given header's length if there's less than
// two bytes of data available (2nd byte of extension headers is length)
if ( total_len < 2 ) return;
current_type = next_type;
IPv6_Hdr* p = new IPv6_Hdr(current_type, hdrs);
next_type = p->NextHdr();
uint16 len = p->Length();
uint16 cur_len = p->Length();
// If this header is truncated, don't add it to chain, don't go further
if ( cur_len > total_len )
{
delete p;
return;
}
if ( set_next && next_type == IPPROTO_FRAGMENT )
{
@ -444,16 +459,17 @@ void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, bool set_next, uint16 next)
// Check for routing headers and remember final destination address.
if ( current_type == IPPROTO_ROUTING )
ProcessRoutingHeader((const struct ip6_rthdr*) hdrs, len);
ProcessRoutingHeader((const struct ip6_rthdr*) hdrs, cur_len);
#ifdef ENABLE_MOBILE_IPV6
// Only Mobile IPv6 has a destination option we care about right now.
if ( current_type == IPPROTO_DSTOPTS )
ProcessDstOpts((const struct ip6_dest*) hdrs, len);
ProcessDstOpts((const struct ip6_dest*) hdrs, cur_len);
#endif
hdrs += len;
length += len;
hdrs += cur_len;
length += cur_len;
total_len -= cur_len;
} while ( current_type != IPPROTO_FRAGMENT &&
current_type != IPPROTO_ESP &&
#ifdef ENABLE_MOBILE_IPV6

View file

@ -142,12 +142,12 @@ public:
/**
* Initializes the header chain from an IPv6 header structure.
*/
IPv6_Hdr_Chain(const struct ip6_hdr* ip6) :
IPv6_Hdr_Chain(const struct ip6_hdr* ip6, int len) :
#ifdef ENABLE_MOBILE_IPV6
homeAddr(0),
#endif
finalDst(0)
{ Init(ip6, false); }
{ Init(ip6, len, false); }
~IPv6_Hdr_Chain()
{
@ -250,14 +250,20 @@ protected:
* Initializes the header chain from an IPv6 header structure, and replaces
* the first next protocol pointer field that points to a fragment header.
*/
IPv6_Hdr_Chain(const struct ip6_hdr* ip6, uint16 next) :
IPv6_Hdr_Chain(const struct ip6_hdr* ip6, uint16 next, int len) :
#ifdef ENABLE_MOBILE_IPV6
homeAddr(0),
#endif
finalDst(0)
{ Init(ip6, true, next); }
{ Init(ip6, len, true, next); }
void Init(const struct ip6_hdr* ip6, bool set_next, uint16 next = 0);
/**
* Initializes the header chain from an IPv6 header structure of a given
* length, possibly setting the first next protocol pointer field that
* points to a fragment header.
*/
void Init(const struct ip6_hdr* ip6, int total_len, bool set_next,
uint16 next = 0);
/**
* Process a routing header and allocate/remember the final destination
@ -294,9 +300,21 @@ protected:
IPAddr* finalDst;
};
/**
* A class that wraps either an IPv4 or IPv6 packet and abstracts methods
* for inquiring about common features between the two.
*/
class IP_Hdr {
public:
IP_Hdr(const u_char* p, bool arg_del)
/**
* Attempts to construct the header from some blob of data based on IP
* version number. Caller must have already checked that the header
* is not truncated.
* @param p pointer to memory containing an IPv4 or IPv6 packet.
* @param arg_del whether to take ownership of \a p pointer's memory.
* @param len the length of data, in bytes, pointed to by \a p.
*/
IP_Hdr(const u_char* p, bool arg_del, int len)
: ip4(0), ip6(0), del(arg_del), ip6_hdrs(0)
{
if ( ((const struct ip*)p)->ip_v == 4 )
@ -304,7 +322,7 @@ public:
else if ( ((const struct ip*)p)->ip_v == 6 )
{
ip6 = (const struct ip6_hdr*)p;
ip6_hdrs = new IPv6_Hdr_Chain(ip6);
ip6_hdrs = new IPv6_Hdr_Chain(ip6, len);
}
else
{
@ -314,18 +332,38 @@ public:
}
}
/**
* Construct the header wrapper from an IPv4 packet. Caller must have
* already checked that the header is not truncated.
* @param arg_ip4 pointer to memory containing an IPv4 packet.
* @param arg_del whether to take ownership of \a arg_ip4 pointer's memory.
*/
IP_Hdr(const struct ip* arg_ip4, bool arg_del)
: ip4(arg_ip4), ip6(0), del(arg_del), ip6_hdrs(0)
{
}
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del,
/**
* Construct the header wrapper from an IPv6 packet. Caller must have
* already checked that the static IPv6 header is not truncated. If
* the packet contains extension headers and they are truncated, that can
* be checked afterwards by comparing \a len with \a TotalLen. E.g.
* NetSessions::DoNextPacket does this to skip truncated packets.
* @param arg_ip6 pointer to memory containing an IPv6 packet.
* @param arg_del whether to take ownership of \a arg_ip6 pointer's memory.
* @param len the packet's length in bytes.
* @param c an already-constructed header chain to take ownership of.
*/
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del, int len,
const IPv6_Hdr_Chain* c = 0)
: ip4(0), ip6(arg_ip6), del(arg_del),
ip6_hdrs(c ? c : new IPv6_Hdr_Chain(ip6))
ip6_hdrs(c ? c : new IPv6_Hdr_Chain(ip6, len))
{
}
/**
* Destructor.
*/
~IP_Hdr()
{
if ( ip6 )
@ -340,8 +378,14 @@ public:
}
}
/**
* If an IPv4 packet is wrapped, return a pointer to it, else null.
*/
const struct ip* IP4_Hdr() const { return ip4; }
/**
* If an IPv6 packet is wrapped, return a pointer to it, else null.
*/
const struct ip6_hdr* IP6_Hdr() const { return ip6; }
/**
@ -441,9 +485,15 @@ public:
{ return ip4 ? ip4->ip_p :
((*ip6_hdrs)[ip6_hdrs->Size()-1])->NextHdr(); }
/**
* Returns the IPv4 Time to Live or IPv6 Hop Limit field.
*/
unsigned char TTL() const
{ return ip4 ? ip4->ip_ttl : ip6->ip6_hlim; }
/**
* Returns whether the IP header indicates this packet is a fragment.
*/
bool IsFragment() const
{ return ip4 ? (ntohs(ip4->ip_off) & 0x3fff) != 0 :
ip6_hdrs->IsFragment(); }

View file

@ -28,8 +28,8 @@ PacketSortElement::PacketSortElement(PktSrc* arg_src,
const struct ip* ip = (const struct ip*) (pkt + hdr_size);
if ( ip->ip_v == 4 )
ip_hdr = new IP_Hdr(ip, false);
else if ( ip->ip_v == 6 )
ip_hdr = new IP_Hdr((const struct ip6_hdr*) ip, false);
else if ( ip->ip_v == 6 && (caplen >= sizeof(struct ip6_hdr) + hdr_size) )
ip_hdr = new IP_Hdr((const struct ip6_hdr*) ip, false, caplen - hdr_size);
else
// Weird will be generated later in NetSessions::NextPacket.
return;

View file

@ -281,7 +281,12 @@ void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
else if ( ip->ip_v == 6 )
{
IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size), false);
if ( caplen < sizeof(struct ip6_hdr) )
{
Weird("truncated_IP", hdr, pkt);
return;
}
IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size), false, caplen);
DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
}