Improve handling of IPv6 routing type 0 extension headers.

- flow_weird event with name argument value of "routing0_hdr" is raised
  for packets containing an IPv6 routing type 0 header because this
  type of header is now deprecated according to RFC 5095.

- packets with a routing type 0 header and non-zero segments left
  now use the last address in that header in order to associate
  with a connection/flow and for calculating TCP/UDP checksums.

- added a set of IPv4/IPv6 TCP/UDP checksum unit tests
This commit is contained in:
Jon Siwek 2012-03-27 16:05:45 -05:00
parent d889f14638
commit f4101b5265
39 changed files with 171 additions and 121 deletions

View file

@ -7,7 +7,6 @@
#include "List.h"
#include "BroList.h"
#include "net_util.h"
class Func;
class FuncType;

View file

@ -28,7 +28,7 @@ void FragTimer::Dispatch(double t, int /* is_expire */)
FragReassembler::FragReassembler(NetSessions* arg_s,
const IP_Hdr* ip, const u_char* pkt,
HashKey* k, double t)
: Reassembler(0, ip->DstAddr(), REASSEM_IP)
: Reassembler(0, REASSEM_IP)
{
s = arg_s;
key = k;

View file

@ -305,6 +305,20 @@ void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, bool set_next, uint16 next)
chain.push_back(p);
// Check for routing type 0 header.
if ( current_type == IPPROTO_ROUTING &&
((const struct ip6_rthdr*)hdrs)->ip6r_type == 0 )
{
if ( ((const struct ip6_rthdr*)hdrs)->ip6r_segleft > 0 )
// Remember the index for later so we can determine the final
// destination of the packet.
route0_hdr_idx = chain.size() - 1;
// RFC 5095 deprecates routing type 0 headers, so raise weirds
IPAddr src(((const struct ip6_hdr*)(chain[0]->Data()))->ip6_src);
reporter->Weird(src, FinalDst(), "routing0_hdr");
}
hdrs += len;
length += len;
} while ( current_type != IPPROTO_FRAGMENT &&

View file

@ -117,7 +117,8 @@ public:
/**
* Initializes the header chain from an IPv6 header structure.
*/
IPv6_Hdr_Chain(const struct ip6_hdr* ip6) { Init(ip6, false); }
IPv6_Hdr_Chain(const struct ip6_hdr* ip6) : route0_hdr_idx(0)
{ Init(ip6, false); }
~IPv6_Hdr_Chain()
{ for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; }
@ -171,6 +172,27 @@ public:
{ return IsFragment() ?
(ntohs(GetFragHdr()->ip6f_offlg) & 0x0001) != 0 : 0; }
/**
* Returns the final destination of the packet this chain belongs to.
* If the chain doesn't contain any routing type 0 header with non-zero
* segments left, this is the destination in the main IP header, else
* it's the last address in the routing header. (If there were to be
* more than one routing type 0 header with non-zero segments left, the
* last one would be the one referenced).
*/
IPAddr FinalDst() const
{
if ( route0_hdr_idx )
{
const struct in6_addr* a = (const struct in6_addr*)
(chain[route0_hdr_idx]->Data() +
chain[route0_hdr_idx]->Length() - 16);
return IPAddr(*a);
}
else
return IPAddr(((const struct ip6_hdr*)(chain[0]->Data()))->ip6_dst);
}
/**
* Returns a vector of ip6_ext_hdr RecordVals that includes script-layer
* representation of all extension headers in the chain.
@ -186,13 +208,24 @@ 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) : route0_hdr_idx(0)
{ Init(ip6, true, next); }
void Init(const struct ip6_hdr* ip6, bool set_next, uint16 next = 0);
vector<IPv6_Hdr*> chain;
uint16 length; // The summation of all header lengths in the chain in bytes.
/**
* The summation of all header lengths in the chain in bytes.
*/
uint16 length;
/**
* Index of routing type 0 header with non-zero segments left in the header
* chain or zero if none exists (it's fine since the main IP header must
* always be at index zero).
*/
uint8 route0_hdr_idx;
};
class IP_Hdr {
@ -248,7 +281,22 @@ public:
IPAddr SrcAddr() const
{ return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); }
IPAddr DstAddr() const
/**
* Returns the final destination address of the header's packet, which
* for IPv6 packets without a routing type 0 extension header and IPv4
* packets is the destination address in the IP header. For IPv6 packets
* with a routing type 0 extension header and a non-zero number of
* segments left, the final destination is the last address in the routing
* header. If the segments left of a routing type 0 header were zero,
* then the final destination is in the IP header itself.
*/
IPAddr FinalDstAddr() const
{ return ip4 ? IPAddr(ip4->ip_dst) : ip6_hdrs->FinalDst(); }
/**
* Returns the destination address held in the IP header.
*/
IPAddr IPHeaderDstAddr() const
{ return ip4 ? IPAddr(ip4->ip_dst) : IPAddr(ip6->ip6_dst); }
/**

View file

@ -58,7 +58,7 @@ bool PacketFilter::Match(const IP_Hdr* ip, int len, int caplen)
if ( f )
return MatchFilter(*f, *ip, len, caplen);
f = (Filter*) dst_filter.Lookup(ip->DstAddr(), 128);
f = (Filter*) dst_filter.Lookup(ip->FinalDstAddr(), 128);
if ( f )
return MatchFilter(*f, *ip, len, caplen);

View file

@ -45,7 +45,7 @@ PacketSortElement::PacketSortElement(PktSrc* arg_src,
(pkt + tcp_offset);
id.src_addr = ip_hdr->SrcAddr();
id.dst_addr = ip_hdr->DstAddr();
id.dst_addr = ip_hdr->FinalDstAddr();
id.src_port = tp->th_sport;
id.dst_port = tp->th_dport;
id.is_one_way = 0;

View file

@ -43,8 +43,7 @@ DataBlock::DataBlock(const u_char* data, int size, int arg_seq,
unsigned int Reassembler::total_size = 0;
Reassembler::Reassembler(int init_seq, const IPAddr& ip_addr,
ReassemblerType arg_type)
Reassembler::Reassembler(int init_seq, ReassemblerType arg_type)
{
blocks = last_block = 0;
trim_seq = last_reassem_seq = init_seq;

View file

@ -26,8 +26,7 @@ enum ReassemblerType { REASSEM_IP, REASSEM_TCP };
class Reassembler : public BroObj {
public:
Reassembler(int init_seq, const IPAddr& ip_addr,
ReassemblerType arg_type);
Reassembler(int init_seq, ReassemblerType arg_type);
virtual ~Reassembler();
void NewBlock(double t, int seq, int len, const u_char* data);

View file

@ -9,7 +9,6 @@
#include <utility>
#include "util.h"
#include "net_util.h"
#include "EventHandler.h"
#include "IPAddr.h"

View file

@ -1105,7 +1105,7 @@ void Packet::Describe(ODesc* d) const
const IP_Hdr ip = IP();
d->Add(ip.SrcAddr());
d->Add("->");
d->Add(ip.DstAddr());
d->Add(ip.FinalDstAddr());
}
bool Packet::Serialize(SerialInfo* info) const

View file

@ -493,7 +493,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
ConnID id;
id.src_addr = ip_hdr->SrcAddr();
id.dst_addr = ip_hdr->DstAddr();
id.dst_addr = ip_hdr->FinalDstAddr();
Dictionary* d = 0;
switch ( proto ) {
@ -667,7 +667,7 @@ FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
ListVal* key = new ListVal(TYPE_ANY);
key->Append(new AddrVal(ip->SrcAddr()));
key->Append(new AddrVal(ip->DstAddr()));
key->Append(new AddrVal(ip->FinalDstAddr()));
key->Append(new Val(frag_id, TYPE_COUNT));
HashKey* h = ch->ComputeHash(key, 1);
@ -1177,7 +1177,7 @@ void NetSessions::Weird(const char* name,
void NetSessions::Weird(const char* name, const IP_Hdr* ip)
{
reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name);
reporter->Weird(ip->SrcAddr(), ip->FinalDstAddr(), name);
}
unsigned int NetSessions::ConnectionMemoryUsage()

View file

@ -31,14 +31,6 @@ TCP_Endpoint::TCP_Endpoint(TCP_Analyzer* arg_analyzer, int arg_is_orig)
tcp_analyzer->Conn()->OrigAddr();
dst_addr = is_orig ? tcp_analyzer->Conn()->OrigAddr() :
tcp_analyzer->Conn()->RespAddr();
checksum_base = ones_complement_checksum(src_addr, 0);
checksum_base = ones_complement_checksum(dst_addr, checksum_base);
// Note, for IPv6, strictly speaking this field is 32 bits
// rather than 16 bits. But because the upper bits are all zero,
// we get the same checksum either way. The same applies to
// later when we add in the data length in ValidChecksum().
checksum_base += htons(IPPROTO_TCP);
}
TCP_Endpoint::~TCP_Endpoint()
@ -108,13 +100,21 @@ void TCP_Endpoint::SizeBufferedData(int& waiting_on_hole, int& waiting_on_ack)
int TCP_Endpoint::ValidChecksum(const struct tcphdr* tp, int len) const
{
uint32 sum = checksum_base;
uint32 sum;
int tcp_len = tp->th_off * 4 + len;
if ( len % 2 == 1 )
// Add in pad byte.
sum += htons(((const u_char*) tp)[tcp_len - 1] << 8);
sum = htons(((const u_char*) tp)[tcp_len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum(src_addr, sum);
sum = ones_complement_checksum(dst_addr, sum);
// Note, for IPv6, strictly speaking the protocol and length fields are
// 32 bits rather than 16 bits. But because the upper bits are all zero,
// we get the same checksum either way.
sum += htons(IPPROTO_TCP);
sum += htons((unsigned short) tcp_len); // fill out pseudo header
sum = ones_complement_checksum((void*) tp, tcp_len, sum);

View file

@ -127,7 +127,6 @@ public:
TCP_Reassembler* contents_processor;
TCP_Analyzer* tcp_analyzer;
BroFile* contents_file;
uint32 checksum_base;
double start_time, last_time;
IPAddr src_addr; // the other endpoint

View file

@ -29,7 +29,7 @@ TCP_Reassembler::TCP_Reassembler(Analyzer* arg_dst_analyzer,
TCP_Analyzer* arg_tcp_analyzer,
TCP_Reassembler::Type arg_type,
bool arg_is_orig, TCP_Endpoint* arg_endp)
: Reassembler(1, arg_endp->dst_addr, REASSEM_TCP)
: Reassembler(1, REASSEM_TCP)
{
dst_analyzer = arg_dst_analyzer;
tcp_analyzer = arg_tcp_analyzer;

View file

@ -57,12 +57,14 @@ void UDP_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig,
{
bool bad = false;
if ( ip->IP4_Hdr() && chksum &&
udp_checksum(ip->IP4_Hdr(), up, len) != 0xffff )
bad = true;
if ( ip->IP4_Hdr() )
{
if ( chksum && ! ValidateChecksum(ip, up, len) )
bad = true;
}
if ( ip->IP6_Hdr() && /* checksum is not optional for IPv6 */
udp6_checksum(ip->IP6_Hdr(), up, len) != 0xffff )
/* checksum is not optional for IPv6 */
else if ( ! ValidateChecksum(ip, up, len) )
bad = true;
if ( bad )
@ -204,4 +206,24 @@ unsigned int UDP_Analyzer::MemoryAllocation() const
return Analyzer::MemoryAllocation() + padded_sizeof(*this) - 24;
}
bool UDP_Analyzer::ValidateChecksum(const IP_Hdr* ip, const udphdr* up, int len)
{
uint32 sum;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) up)[len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum(ip->SrcAddr(), sum);
sum = ones_complement_checksum(ip->FinalDstAddr(), sum);
// Note, for IPv6, strictly speaking the protocol and length fields are
// 32 bits rather than 16 bits. But because the upper bits are all zero,
// we get the same checksum either way.
sum += htons(IPPROTO_UDP);
sum += htons((unsigned short) len);
sum = ones_complement_checksum((void*) up, len, sum);
return sum == 0xffff;
}

View file

@ -4,6 +4,7 @@
#define udp_h
#include "Analyzer.h"
#include <netinet/udp.h>
typedef enum {
UDP_INACTIVE, // no packet seen
@ -31,6 +32,10 @@ protected:
virtual bool IsReuse(double t, const u_char* pkt);
virtual unsigned int MemoryAllocation() const;
// Returns true if the checksum is valid, false if not
static bool ValidateChecksum(const IP_Hdr* ip, const struct udphdr* up,
int len);
bro_int_t request_len, reply_len;
private:

View file

@ -4798,6 +4798,9 @@ function uninstall_src_net_filter%(snet: subnet%) : bool
## Installs a filter to drop packets destined to a given IP address with
## a certain probability if none of a given set of TCP flags are set.
## Note that for IPv6 packets with a routing type 0 header and non-zero
## segments left, this filters out against the final destination of the
## packet according to the routing extension header.
##
## ip: Drop packets to this IP address.
##

View file

@ -31,85 +31,6 @@ int ones_complement_checksum(const void* p, int b, uint32 sum)
return sum;
}
int ones_complement_checksum(const IPAddr& a, uint32 sum)
{
const uint32* bytes;
int len = a.GetBytes(&bytes);
return ones_complement_checksum(bytes, len*4, sum);
}
int tcp_checksum(const struct ip* ip, const struct tcphdr* tp, int len)
{
// ### Note, this is only correct for IPv4. This routine is only
// used by the connection compressor (which we turn off for IPv6
// traffic).
int tcp_len = tp->th_off * 4 + len;
uint32 sum;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) tp)[tcp_len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum((void*) &ip->ip_src.s_addr, 4, sum);
sum = ones_complement_checksum((void*) &ip->ip_dst.s_addr, 4, sum);
uint32 addl_pseudo =
(htons(IPPROTO_TCP) << 16) | htons((unsigned short) tcp_len);
sum = ones_complement_checksum((void*) &addl_pseudo, 4, sum);
sum = ones_complement_checksum((void*) tp, tcp_len, sum);
return sum;
}
int udp_checksum(const struct ip* ip, const struct udphdr* up, int len)
{
uint32 sum;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) up)[len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum((void*) &ip->ip_src.s_addr, 4, sum);
sum = ones_complement_checksum((void*) &ip->ip_dst.s_addr, 4, sum);
uint32 addl_pseudo =
(htons(IPPROTO_UDP) << 16) | htons((unsigned short) len);
sum = ones_complement_checksum((void*) &addl_pseudo, 4, sum);
sum = ones_complement_checksum((void*) up, len, sum);
return sum;
}
int udp6_checksum(const struct ip6_hdr* ip6, const struct udphdr* up, int len)
{
uint32 sum;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) up)[len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum((void*) ip6->ip6_src.s6_addr, 16, sum);
sum = ones_complement_checksum((void*) ip6->ip6_dst.s6_addr, 16, sum);
uint32 l = htonl(len);
sum = ones_complement_checksum((void*) &l, 4, sum);
uint32 addl_pseudo = htons(IPPROTO_UDP);
sum = ones_complement_checksum((void*) &addl_pseudo, 4, sum);
sum = ones_complement_checksum((void*) up, len, sum);
return sum;
}
int icmp_checksum(const struct icmp* icmpp, int len)
{
uint32 sum;

View file

@ -60,12 +60,14 @@ inline int seq_delta(uint32 a, uint32 b)
// Returns the ones-complement checksum of a chunk of b short-aligned bytes.
extern int ones_complement_checksum(const void* p, int b, uint32 sum);
extern int ones_complement_checksum(const IPAddr& a, uint32 sum);
extern int tcp_checksum(const struct ip* ip, const struct tcphdr* tp, int len);
extern int udp_checksum(const struct ip* ip, const struct udphdr* up, int len);
extern int udp6_checksum(const struct ip6_hdr* ip, const struct udphdr* up,
int len);
inline int ones_complement_checksum(const IPAddr& a, uint32 sum)
{
const uint32* bytes;
int len = a.GetBytes(&bytes);
return ones_complement_checksum(bytes, len*4, sum);
}
extern int icmp_checksum(const struct icmp* icmpp, int len);
// Returns 'A', 'B', 'C' or 'D'