Add IPv6 fragment reassembly.

This commit is contained in:
Jon Siwek 2012-03-06 16:08:28 -06:00
parent a0e07018f4
commit 9d590456b0
4 changed files with 180 additions and 61 deletions

View file

@ -33,13 +33,23 @@ FragReassembler::FragReassembler(NetSessions* arg_s,
s = arg_s;
key = k;
const struct ip* ip4 = ip->IP4_Hdr();
if ( ip4 )
{
proto_hdr_len = ip4->ip_hl * 4;
proto_hdr = (struct ip*) new u_char[64]; // max IP header + slop
proto_hdr = new u_char[64]; // max IP header + slop
// Don't do a structure copy - need to pick up options, too.
memcpy((void*) proto_hdr, (const void*) ip4, proto_hdr_len);
}
else
{
proto_hdr_len = ip->HdrLen() - 8; // minus length of fragment header
proto_hdr = new u_char[proto_hdr_len];
memcpy(proto_hdr, ip->IP6_Hdr(), proto_hdr_len);
}
reassembled_pkt = 0;
frag_size = 0; // flag meaning "not known"
next_proto = ip->NextProto();
AddFragment(t, ip, pkt);
@ -64,22 +74,37 @@ void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt)
{
const struct ip* ip4 = ip->IP4_Hdr();
if ( ip4->ip_p != proto_hdr->ip_p || ip4->ip_hl != proto_hdr->ip_hl )
if ( ip4 )
{
if ( ip4->ip_p != ((const struct ip*)proto_hdr)->ip_p ||
ip4->ip_hl != ((const struct ip*)proto_hdr)->ip_hl )
// || ip4->ip_tos != proto_hdr->ip_tos
// don't check TOS, there's at least one stack that actually
// uses different values, and it's hard to see an associated
// attack.
s->Weird("fragment_protocol_inconsistency", ip);
}
else
{
if ( ip->NextProto() != next_proto ||
ip->HdrLen() - 8 != proto_hdr_len )
s->Weird("fragment_protocol_inconsistency", ip);
//TODO: more detailed unfrag header consistency checks?
}
if ( ip->DF() )
// Linux MTU discovery for UDP can do this, for example.
s->Weird("fragment_with_DF", ip);
int offset = ip->FragOffset();
int len = ntohs(ip4->ip_len);
int hdr_len = proto_hdr->ip_hl * 4;
int len = ip->TotalLen();
int hdr_len = ip->HdrLen();
int upper_seq = offset + len - hdr_len;
if ( ! offset )
// Make sure to use the first fragment header's next field.
next_proto = ip->NextProto();
if ( ! ip->MF() )
{
// Last fragment.
@ -192,8 +217,7 @@ void FragReassembler::BlockInserted(DataBlock* /* start_block */)
u_char* pkt = new u_char[n];
memcpy((void*) pkt, (const void*) proto_hdr, proto_hdr_len);
struct ip* reassem4 = (struct ip*) pkt;
reassem4->ip_len = htons(frag_size + proto_hdr_len);
u_char* pkt_start = pkt;
pkt += proto_hdr_len;
@ -213,7 +237,20 @@ void FragReassembler::BlockInserted(DataBlock* /* start_block */)
}
delete reassembled_pkt;
if ( ((const struct ip*)pkt_start)->ip_v == 4 )
{
struct ip* reassem4 = (struct ip*) pkt_start;
reassem4->ip_len = htons(frag_size + proto_hdr_len);
reassembled_pkt = new IP_Hdr(reassem4, true);
}
else
{
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);
}
DeleteTimer();
}

View file

@ -36,11 +36,12 @@ protected:
void BlockInserted(DataBlock* start_block);
void Overlap(const u_char* b1, const u_char* b2, int n);
struct ip* proto_hdr;
u_char* proto_hdr;
IP_Hdr* reassembled_pkt;
int proto_hdr_len;
NetSessions* s;
int frag_size; // size of fully reassembled fragment
uint16 next_proto; // first IPv6 fragment header's next proto field
HashKey* key;
FragTimer* expire_timer;

View file

@ -26,7 +26,7 @@ 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(0, new Val((ntohl(ip6->ip6_flow) & 0x0ff00000)>>20, 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));
@ -96,8 +96,8 @@ RecordVal* IPv6_Fragment::BuildRecordVal() const
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(2, new Val((ntohs(frag->ip6f_offlg) & 0xfff8)>>3, TYPE_COUNT));
rv->Assign(3, new Val((ntohs(frag->ip6f_offlg) & 0x0006)>>1, 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;
@ -210,23 +210,24 @@ RecordVal* IP_Hdr::BuildRecordVal() const
return rval;
}
static inline IPv6_Hdr* getIPv6Header(uint8 type, const u_char* d)
static inline IPv6_Hdr* getIPv6Header(uint8 type, const u_char* d,
bool set_next = false, uint16 nxt = 0)
{
switch (type) {
case IPPROTO_IPV6:
return new IPv6_Hdr(d);
return set_next ? new IPv6_Hdr(d, nxt) : new IPv6_Hdr(d);
case IPPROTO_HOPOPTS:
return new IPv6_HopOpts(d);
return set_next ? new IPv6_HopOpts(d, nxt) : new IPv6_HopOpts(d);
case IPPROTO_ROUTING:
return new IPv6_Routing(d);
return set_next ? new IPv6_Routing(d, nxt) : new IPv6_Routing(d);
case IPPROTO_DSTOPTS:
return new IPv6_DstOpts(d);
return set_next ? new IPv6_DstOpts(d, nxt) : new IPv6_DstOpts(d);
case IPPROTO_FRAGMENT:
return new IPv6_Fragment(d);
return set_next ? new IPv6_Fragment(d, nxt) : new IPv6_Fragment(d);
case IPPROTO_AH:
return new IPv6_AH(d);
return set_next ? new IPv6_AH(d, nxt) : new IPv6_AH(d);
case IPPROTO_ESP:
return new IPv6_ESP(d);
return new IPv6_ESP(d); // never able to set ESP header's next
default:
// should never get here if calls are protected by isIPv6ExtHeader()
reporter->InternalError("Unknown IPv6 header type: %d", type);
@ -252,7 +253,7 @@ static inline bool isIPv6ExtHeader(uint8 type)
}
}
IPv6_Hdr_Chain::IPv6_Hdr_Chain(const struct ip6_hdr* ip6)
void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, bool set_next, uint16 next)
{
length = 0;
uint8 current_type, next_type;
@ -262,7 +263,7 @@ IPv6_Hdr_Chain::IPv6_Hdr_Chain(const struct ip6_hdr* ip6)
do
{
current_type = next_type;
chain.push_back(getIPv6Header(current_type, hdrs));
chain.push_back(getIPv6Header(current_type, hdrs, set_next, next));
next_type = chain[chain.size()-1]->NextHdr();
uint16 len = chain[chain.size()-1]->Length();
hdrs += len;

154
src/IP.h
View file

@ -26,6 +26,16 @@ public:
*/
IPv6_Hdr(const u_char* d) : type(IPPROTO_IPV6), data(d) {}
/**
* Construct the main IPv6 header, but replace the next protocol field
* if it points to a fragment.
*/
IPv6_Hdr(const u_char* d, uint16 nxt) : type(IPPROTO_IPV6), data(d)
{
if ( ((ip6_hdr*)data)->ip6_nxt == IPPROTO_FRAGMENT )
((ip6_hdr*)data)->ip6_nxt = nxt;
}
/**
* Construct an IPv6 header or extension header from assigned type number.
*/
@ -49,6 +59,11 @@ public:
*/
uint8 Type() const { return type; }
/**
* Returns pointer to the start of where header structure resides in memory.
*/
const u_char* Data() const { return data; }
/**
* Returns the script-layer record representation of the header.
*/
@ -59,50 +74,63 @@ protected:
const u_char* data;
};
class IPv6_HopOpts : public IPv6_Hdr {
class IPv6_Ext : public IPv6_Hdr {
public:
IPv6_HopOpts(const u_char* d) : IPv6_Hdr(IPPROTO_HOPOPTS, d) {}
IPv6_Ext(uint16 type, const u_char* d) : IPv6_Hdr(type, d) {}
IPv6_Ext(uint16 type, const u_char* d, uint16 nxt) : IPv6_Hdr(type, d)
{
if ( ((ip6_ext*)data)->ip6e_nxt == IPPROTO_FRAGMENT )
((ip6_ext*)data)->ip6e_nxt = nxt;
}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
virtual uint16 Length() const = 0;
virtual RecordVal* BuildRecordVal() const = 0;
};
class IPv6_HopOpts : public IPv6_Ext {
public:
IPv6_HopOpts(const u_char* d) : IPv6_Ext(IPPROTO_HOPOPTS, d) {}
IPv6_HopOpts(const u_char* d, uint16 n) : IPv6_Ext(IPPROTO_HOPOPTS, d, n) {}
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
RecordVal* BuildRecordVal() const;
};
class IPv6_DstOpts : public IPv6_Hdr {
class IPv6_DstOpts : public IPv6_Ext {
public:
IPv6_DstOpts(const u_char* d) : IPv6_Hdr(IPPROTO_DSTOPTS, d) {}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
IPv6_DstOpts(const u_char* d) : IPv6_Ext(IPPROTO_DSTOPTS, d) {}
IPv6_DstOpts(const u_char* d, uint16 n) : IPv6_Ext(IPPROTO_DSTOPTS, d, n) {}
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
RecordVal* BuildRecordVal() const;
};
class IPv6_Routing : public IPv6_Hdr {
class IPv6_Routing : public IPv6_Ext {
public:
IPv6_Routing(const u_char* d) : IPv6_Hdr(IPPROTO_ROUTING, d) {}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
IPv6_Routing(const u_char* d) : IPv6_Ext(IPPROTO_ROUTING, d) {}
IPv6_Routing(const u_char* d, uint16 n) : IPv6_Ext(IPPROTO_ROUTING, d, n) {}
uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; }
RecordVal* BuildRecordVal() const;
};
class IPv6_Fragment : public IPv6_Hdr {
class IPv6_Fragment : public IPv6_Ext {
public:
IPv6_Fragment(const u_char* d) : IPv6_Hdr(IPPROTO_FRAGMENT, d) {}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
IPv6_Fragment(const u_char* d) : IPv6_Ext(IPPROTO_FRAGMENT, d) {}
IPv6_Fragment(const u_char* d, uint16 n) : IPv6_Ext(IPPROTO_FRAGMENT, d, n)
{}
uint16 Length() const { return 8; }
RecordVal* BuildRecordVal() const;
};
class IPv6_AH : public IPv6_Hdr {
class IPv6_AH : public IPv6_Ext {
public:
IPv6_AH(const u_char* d) : IPv6_Hdr(IPPROTO_AH, d) {}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
IPv6_AH(const u_char* d) : IPv6_Ext(IPPROTO_AH, d) {}
IPv6_AH(const u_char* d, uint16 n) : IPv6_Ext(IPPROTO_AH, d, n) {}
uint16 Length() const { return 8 + 4 * ((ip6_ext*)data)->ip6e_len; }
RecordVal* BuildRecordVal() const;
};
class IPv6_ESP : public IPv6_Hdr {
class IPv6_ESP : public IPv6_Ext {
public:
IPv6_ESP(const u_char* d) : IPv6_Hdr(IPPROTO_ESP, d) {}
uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; }
IPv6_ESP(const u_char* d) : IPv6_Ext(IPPROTO_ESP, d) {}
// encrypted payload begins after 8 bytes
uint16 Length() const { return 8; }
RecordVal* BuildRecordVal() const;
@ -113,7 +141,14 @@ 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) { Init(ip6, false); }
/**
* 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)
{ Init(ip6, true, next); }
~IPv6_Hdr_Chain()
{ for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; }
@ -133,22 +168,73 @@ public:
*/
const IPv6_Hdr* operator[](const size_t i) const { return chain[i]; }
/**
* Returns whether the header chain indicates a fragmented packet.
*/
bool IsFragment() const
{ return chain[chain.size()-1]->Type() == IPPROTO_FRAGMENT; }
/**
* Returns pointer to fragment header structure if the chain contains one.
*/
const struct ip6_frag* GetFragHdr() const
{ return IsFragment() ?
(const struct ip6_frag*)chain[chain.size()-1]->Data(): 0; }
/**
* If the header chain is a fragment, returns the offset in number of bytes
* relative to the start of the Fragmentable Part of the original packet.
*/
uint16 FragOffset() const
{ return IsFragment() ?
(ntohs(GetFragHdr()->ip6f_offlg) & 0xfff8) : 0; }
/**
* If the header chain is a fragment, returns the identification field.
*/
uint32 ID() const
{ return IsFragment() ? ntohl(GetFragHdr()->ip6f_ident) : 0; }
/**
* If the header chain is a fragment, returns the M (more fragments) flag.
*/
int MF() const
{ return IsFragment() ?
(ntohs(GetFragHdr()->ip6f_offlg) & 0x0001) != 0 : 0; }
protected:
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.
};
class IP_Hdr {
public:
IP_Hdr(const u_char* p, bool arg_del)
: ip4(0), ip6(0), del(arg_del), ip6_hdrs(0)
{
if ( ((const struct ip*)p)->ip_v == 4 )
ip4 = (const struct ip*)p;
else if ( ((const struct ip*)p)->ip_v == 6 )
{
ip6 = (const struct ip6_hdr*)p;
ip6_hdrs = new IPv6_Hdr_Chain(ip6);
}
else if ( arg_del )
delete [] p;
}
IP_Hdr(const struct ip* arg_ip4, bool arg_del)
: ip4(arg_ip4), ip6(0), del(arg_del)
: ip4(arg_ip4), ip6(0), del(arg_del), ip6_hdrs(0)
{
}
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del)
: ip4(0), ip6(arg_ip6), del(arg_del)
IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del,
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 = new IPv6_Hdr_Chain(ip6);
}
~IP_Hdr()
@ -190,7 +276,7 @@ public:
return ntohs(ip6->ip6_plen) - ip6_hdrs->TotalLength();
}
uint16 TotalLen() const
uint32 TotalLen() const
{ return ip4 ? ntohs(ip4->ip_len) : ntohs(ip6->ip6_plen) + 40; }
uint16 HdrLen() const
@ -207,25 +293,19 @@ public:
unsigned char TTL() const
{ 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; }
{ return ip4 ? (ntohs(ip4->ip_off) & 0x3fff) != 0 :
ip6_hdrs->IsFragment(); }
//TODO: check for IPv6 Fragment ext. header
uint16 FragOffset() const
{ return ip4 ? (ntohs(ip4->ip_off) & 0x1fff) * 8 : 0; }
{ return ip4 ? (ntohs(ip4->ip_off) & 0x1fff) * 8 :
ip6_hdrs->FragOffset(); }
//TODO: check for IPv6 Fragment ext. header
uint16 FragField() const
{ return ip4 ? ntohs(ip4->ip_off) : 0; }
uint32 ID() const
{ return ip4 ? ntohs(ip4->ip_id) : ip6_hdrs->ID(); }
//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; }
{ return ip4 ? (ntohs(ip4->ip_off) & 0x2000) != 0 : ip6_hdrs->MF(); }
// IPv6 has no "Don't Fragment" flag.
int DF() const
@ -240,7 +320,7 @@ private:
const struct ip* ip4;
const struct ip6_hdr* ip6;
bool del;
IPv6_Hdr_Chain* ip6_hdrs;
const IPv6_Hdr_Chain* ip6_hdrs;
};
#endif