diff --git a/src/Frag.cc b/src/Frag.cc index b5c5e371d4..cbdae92883 100644 --- a/src/Frag.cc +++ b/src/Frag.cc @@ -33,13 +33,23 @@ FragReassembler::FragReassembler(NetSessions* arg_s, s = arg_s; key = k; const struct ip* ip4 = ip->IP4_Hdr(); - proto_hdr_len = ip4->ip_hl * 4; - proto_hdr = (struct ip*) 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); + if ( ip4 ) + { + proto_hdr_len = ip4->ip_hl * 4; + 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; - reassembled_pkt = new IP_Hdr(reassem4, true); + + 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(); } diff --git a/src/Frag.h b/src/Frag.h index 4c9886faa2..86cf3a9dd4 100644 --- a/src/Frag.h +++ b/src/Frag.h @@ -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; diff --git a/src/IP.cc b/src/IP.cc index 826ae544f6..ce8514519a 100644 --- a/src/IP.cc +++ b/src/IP.cc @@ -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; diff --git a/src/IP.h b/src/IP.h index 09640f47b9..be3d568375 100644 --- a/src/IP.h +++ b/src/IP.h @@ -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 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