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 )
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'

View file

@ -0,0 +1,9 @@
1332784981.078396 weird: bad_IP_checksum
1332784885.686428 weird: bad_TCP_checksum
1332784933.501023 weird: bad_UDP_checksum
1332785210.013051 weird: routing0_hdr
1332785210.013051 weird: bad_TCP_checksum
1332782580.798420 weird: routing0_hdr
1332782580.798420 weird: bad_UDP_checksum
1332785250.469132 weird: bad_TCP_checksum
1332781342.923813 weird: bad_UDP_checksum

View file

@ -0,0 +1,2 @@
1332785125.596793 weird: routing0_hdr
1332782508.592037 weird: routing0_hdr

View file

@ -1 +1,3 @@
weird routing0_hdr from 2001:4f8:4:7:2e0:81ff:fe52:ffff to 2001:78:1:32::2
[orig_h=2001:4f8:4:7:2e0:81ff:fe52:ffff, orig_p=53/udp, resp_h=2001:78:1:32::2, resp_p=53/udp]
[ip=<uninitialized>, ip6=[class=0, flow=0, len=59, nxt=0, hlim=64, src=2001:4f8:4:7:2e0:81ff:fe52:ffff, dst=2001:4f8:4:7:2e0:81ff:fe52:9a6b, exts=[[id=0, hopopts=[nxt=43, len=0, options=[[otype=1, len=4, data=\0\0\0\0]]], dstopts=<uninitialized>, routing=<uninitialized>, fragment=<uninitialized>, ah=<uninitialized>, esp=<uninitialized>], [id=43, hopopts=<uninitialized>, dstopts=<uninitialized>, routing=[nxt=17, len=4, rtype=0, segleft=2, data=\0\0\0\0 ^A\0x\0^A\02\0\0\0\0\0\0\0^A ^A\0x\0^A\02\0\0\0\0\0\0\0^B], fragment=<uninitialized>, ah=<uninitialized>, esp=<uninitialized>]]], tcp=<uninitialized>, udp=[sport=53/udp, dport=53/udp, ulen=11], icmp=<uninitialized>]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,4 @@
# @TEST-EXEC: bro -C -b -r $TRACES/ext_hdr_hbh_routing.trace %INPUT >output
# @TEST-EXEC: bro -b -r $TRACES/ipv6-hbh-routing0.trace %INPUT >output
# @TEST-EXEC: btest-diff output
event ipv6_ext_headers(c: connection, p: pkt_hdr)

View file

@ -0,0 +1,15 @@
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip4-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip4-tcp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip4-udp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-route0-tcp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-route0-udp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-tcp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-udp-bad-chksum.pcap >>bad.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip4-tcp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip4-udp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-route0-tcp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-route0-udp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-tcp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-udp-good-chksum.pcap >>good.out 2>&1
# @TEST-EXEC: btest-diff bad.out
# @TEST-EXEC: btest-diff good.out

View file

@ -1,10 +1,22 @@
# @TEST-EXEC: bro -C -b -r $TRACES/ext_hdr_hbh_routing.trace %INPUT >output
# @TEST-EXEC: bro -b -r $TRACES/ipv6-hbh-routing0.trace %INPUT >output
# @TEST-EXEC: btest-diff output
# Just check that the event is raised correctly for a packet containing
# extension headers.
event ipv6_ext_headers(c: connection, p: pkt_hdr)
{
print p;
}
# Also check the weird for routing type 0 extensions headers
event flow_weird(name: string, src: addr, dst: addr)
{
print fmt("weird %s from %s to %s", name, src, dst);
}
# And the connection for routing type 0 packets with non-zero segments left
# should use the last address in that extension header.
event new_connection(c: connection)
{
print c$id;
}