diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index c4ae4b134a..91c6b7856d 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -939,11 +939,154 @@ const IPPROTO_IGMP = 2; ##< Group management protocol. const IPPROTO_IPIP = 4; ##< IP encapsulation in IP. const IPPROTO_TCP = 6; ##< TCP. const IPPROTO_UDP = 17; ##< User datagram protocol. +const IPPROTO_IPV6 = 41; ##< IPv6 header. const IPPROTO_RAW = 255; ##< Raw IP packet. -## Values extracted from an IP header. +# Definitions for IPv6 extension headers. +const IPPROTO_HOPOPTS = 0; ##< IPv6 hop-by-hop-options header. +const IPPROTO_ROUTING = 43; ##< IPv6 routing header. +const IPPROTO_FRAGMENT = 44; ##< IPv6 fragment header. +const IPPROTO_ESP = 50; ##< IPv6 encapsulating security payload header. +const IPPROTO_AH = 51; ##< IPv6 authentication header. +const IPPROTO_NONE = 59; ##< IPv6 no next header. +const IPPROTO_DSTOPTS = 60; ##< IPv6 destination options header. + +## Values extracted from an IPv6 header. ## -## .. bro:see:: pkt_hdr discarder_check_ip +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr_chain ip6_hopopts ip6_dstopts ip6_routing +## ip6_fragment ip6_ah ip6_esp +type ip6_hdr: record { + class: count; ##< Traffic class. + flow: count; ##< Flow label. + len: count; ##< Payload length. + nxt: count; ##< Next header (RFC 1700 assigned number). + hlim: count; ##< Hop limit. + src: addr; ##< Source address. + dst: addr; ##< Destination address. +}; + +## Values extracted from an IPv6 extension header's (e.g. hop-by-hop or +## destination option headers) option field. +## +## .. bro:see:: ip6_hdr ip6_hdr_chain ip6_hopopts ip6_dstopts +type ip6_option: record { + otype: count; ##< Option type. + len: count; ##< Option data length. + data: string; ##< Option data. +}; + +## Values extracted from an IPv6 Hop-by-Hop options extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain ip6_option +type ip6_hopopts: record { + ## Next header (RFC 1700 assigned number). + nxt: count; + ## Length of header in 8-octet units, excluding first unit. + len: count; + ## The TLV encoded options; + options: vector of ip6_option; +}; + +## Values extracted from an IPv6 Destination options extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain ip6_option +type ip6_dstopts: record { + ## Next header (RFC 1700 assigned number). + nxt: count; + ## Length of header in 8-octet units, excluding first unit. + len: count; + ## The TLV encoded options; + options: vector of ip6_option; +}; + +## Values extracted from an IPv6 Routing extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain +type ip6_routing: record { + ## Next header (RFC 1700 assigned number). + nxt: count; + ## Length of header in 8-octet units, excluding first unit. + len: count; + ## Routing type. + rtype: count; + ## Segments left. + segleft: count; + ## Type-specific data. + data: string; +}; + +## Values extracted from an IPv6 Fragment extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain +type ip6_fragment: record { + ## Next header (RFC 1700 assigned number). + nxt: count; + ## 8-bit reserved field. + rsv1: count; + ## Fragmentation offset. + offset: count; + ## 2-bit reserved field. + rsv2: count; + ## More fragments. + more: bool; + ## Fragment identification. + id: count; +}; + +## Values extracted from an IPv6 Authentication extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain +type ip6_ah: record { + ## Next header (RFC 1700 assigned number). + nxt: count; + ## Length of header in 4-octet units, excluding first two units. + len: count; + ## Reserved field. + rsv: count; + ## Security Parameter Index. + spi: count; + ## Sequence number. + seq: count; + ## Authentication data. + data: string; +}; + +## Values extracted from an IPv6 ESP extension header. +## +## .. bro:see:: pkt_hdr ip_hdr ip6_hdr ip6_hdr_chain +type ip6_esp: record { + ## Security Parameters Index. + spi: count; + ## Sequence number. + seq: count; +}; + +## An IPv6 header chain. +## +## .. bro:see:: pkt_hdr ip_hdr +type ip6_hdr_chain: record { + ## The main IPv6 header. + hdr: ip6_hdr; + ## Hop-by-hop option extension header. + hopopts: vector of ip6_hopopts; + ## Destination option extension headers. + dstopts: vector of ip6_dstopts; + ## Routing extension headers. + routing: vector of ip6_routing; + ## Fragment headers. + fragment: vector of ip6_fragment; + ## Authentication extension headers. + ah: vector of ip6_ah; + ## Encapsulating security payload headers. + esp: vector of ip6_esp; + + ## Order of extension headers identified by RFC 1700 assigned numbers. + ext_order: vector of count; +}; + +## Values extracted from an IPv4 header. +## +## .. bro:see:: pkt_hdr ip6_hdr discarder_check_ip type ip_hdr: record { hl: count; ##< Header length in bytes. tos: count; ##< Type of service. @@ -1000,10 +1143,11 @@ type icmp_hdr: record { ## ## .. bro:see:: new_packet type pkt_hdr: record { - ip: ip_hdr; ##< The IP header. - tcp: tcp_hdr &optional; ##< The TCP header if a TCP packet. - udp: udp_hdr &optional; ##< The UDP header if a UDP packet. - icmp: icmp_hdr &optional; ##< The ICMP header if an ICMP packet. + ip: ip_hdr &optional; ##< The IPv4 header if an IPv4 packet. + ip6: ip6_hdr_chain &optional; ##< The IPv6 header chain if an IPv6 packet. + tcp: tcp_hdr &optional; ##< The TCP header if a TCP packet. + udp: udp_hdr &optional; ##< The UDP header if a UDP packet. + icmp: icmp_hdr &optional; ##< The ICMP header if an ICMP packet. }; ## Definition of "secondary filters". A secondary filter is a BPF filter given as diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d51211f0d1..26807a184f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -330,6 +330,7 @@ set(bro_SRCS IntSet.cc InterConn.cc IOSource.cc + IP.cc IPAddr.cc IRC.cc List.cc diff --git a/src/Frag.cc b/src/Frag.cc index 21abc324f8..b5c5e371d4 100644 --- a/src/Frag.cc +++ b/src/Frag.cc @@ -27,7 +27,7 @@ void FragTimer::Dispatch(double t, int /* is_expire */) FragReassembler::FragReassembler(NetSessions* arg_s, const IP_Hdr* ip, const u_char* pkt, - uint32 frag_field, HashKey* k, double t) + HashKey* k, double t) : Reassembler(0, ip->DstAddr(), REASSEM_IP) { s = arg_s; @@ -41,7 +41,7 @@ FragReassembler::FragReassembler(NetSessions* arg_s, reassembled_pkt = 0; frag_size = 0; // flag meaning "not known" - AddFragment(t, ip, pkt, frag_field); + AddFragment(t, ip, pkt); if ( frag_timeout != 0.0 ) { @@ -60,8 +60,7 @@ FragReassembler::~FragReassembler() delete key; } -void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt, - uint32 frag_field) +void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt) { const struct ip* ip4 = ip->IP4_Hdr(); @@ -72,16 +71,16 @@ void FragReassembler::AddFragment(double t, const IP_Hdr* ip, const u_char* pkt, // attack. s->Weird("fragment_protocol_inconsistency", ip); - if ( frag_field & 0x4000 ) + if ( ip->DF() ) // Linux MTU discovery for UDP can do this, for example. s->Weird("fragment_with_DF", ip); - int offset = (ntohs(ip4->ip_off) & 0x1fff) * 8; + int offset = ip->FragOffset(); int len = ntohs(ip4->ip_len); int hdr_len = proto_hdr->ip_hl * 4; int upper_seq = offset + len - hdr_len; - if ( (frag_field & 0x2000) == 0 ) + if ( ! ip->MF() ) { // Last fragment. if ( frag_size == 0 ) diff --git a/src/Frag.h b/src/Frag.h index 92bf1b3bbd..4c9886faa2 100644 --- a/src/Frag.h +++ b/src/Frag.h @@ -20,11 +20,10 @@ typedef void (FragReassembler::*frag_timer_func)(double t); class FragReassembler : public Reassembler { public: FragReassembler(NetSessions* s, const IP_Hdr* ip, const u_char* pkt, - uint32 frag_field, HashKey* k, double t); + HashKey* k, double t); ~FragReassembler(); - void AddFragment(double t, const IP_Hdr* ip, const u_char* pkt, - uint32 frag_field); + void AddFragment(double t, const IP_Hdr* ip, const u_char* pkt); void Expire(double t); void DeleteTimer(); diff --git a/src/IP.cc b/src/IP.cc new file mode 100644 index 0000000000..826ae544f6 --- /dev/null +++ b/src/IP.cc @@ -0,0 +1,273 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#include "IP.h" +#include "Type.h" +#include "Val.h" +#include "Var.h" + +static RecordType* ip_hdr_type = 0; +static RecordType* ip6_hdr_type = 0; +static RecordType* ip6_hdr_chain_type = 0; +static RecordType* ip6_option_type = 0; +static RecordType* ip6_hopopts_type = 0; +static RecordType* ip6_dstopts_type = 0; +static RecordType* ip6_routing_type = 0; +static RecordType* ip6_fragment_type = 0; +static RecordType* ip6_ah_type = 0; +static RecordType* ip6_esp_type = 0; + +static inline RecordType* hdrType(RecordType*& type, const char* name) + { + if ( ! type ) type = internal_type(name)->AsRecordType(); + return type; + } + +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(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)); + rv->Assign(4, new Val(ip6->ip6_hlim, TYPE_COUNT)); + rv->Assign(5, new AddrVal(ip6->ip6_src)); + rv->Assign(6, new AddrVal(ip6->ip6_dst)); + return rv; + } + +static VectorVal* BuildOptionsVal(const u_char* data, uint16 len) + { + VectorVal* vv = new VectorVal(new VectorType(ip6_option_type->Ref())); + while ( len > 0 ) + { + const struct ip6_opt* opt = (const struct ip6_opt*) data; + RecordVal* rv = new RecordVal(hdrType(ip6_option_type, "ip6_option")); + rv->Assign(0, new Val(opt->ip6o_type, TYPE_COUNT)); + rv->Assign(1, new Val(opt->ip6o_len, TYPE_COUNT)); + uint16 off = 2 * sizeof(uint8); + rv->Assign(2, new StringVal( + new BroString(data + off, opt->ip6o_len - off, 1))); + data += opt->ip6o_len + off; + len -= opt->ip6o_len + off; + vv->Assign(vv->Size(), rv, 0); + } + return vv; + } + +RecordVal* IPv6_HopOpts::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_hopopts_type, "ip6_hopopts")); + const struct ip6_hbh* hbh = (const struct ip6_hbh*)data; + rv->Assign(0, new Val(hbh->ip6h_nxt, TYPE_COUNT)); + rv->Assign(1, new Val(hbh->ip6h_len, TYPE_COUNT)); + uint16 off = 2 * sizeof(uint8); + rv->Assign(2, BuildOptionsVal(data + off, Length() - off)); + return rv; + } + +RecordVal* IPv6_DstOpts::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_dstopts_type, "ip6_dstopts")); + const struct ip6_dest* dst = (const struct ip6_dest*)data; + rv->Assign(0, new Val(dst->ip6d_nxt, TYPE_COUNT)); + rv->Assign(1, new Val(dst->ip6d_len, TYPE_COUNT)); + uint16 off = 2 * sizeof(uint8); + rv->Assign(2, BuildOptionsVal(data + off, Length() - off)); + return rv; + } + +RecordVal* IPv6_Routing::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_routing_type, "ip6_routing")); + const struct ip6_rthdr* rt = (const struct ip6_rthdr*)data; + rv->Assign(0, new Val(rt->ip6r_nxt, TYPE_COUNT)); + rv->Assign(1, new Val(rt->ip6r_len, TYPE_COUNT)); + rv->Assign(2, new Val(rt->ip6r_type, TYPE_COUNT)); + rv->Assign(3, new Val(rt->ip6r_segleft, TYPE_COUNT)); + uint16 off = 4 * sizeof(uint8); + rv->Assign(4, new StringVal(new BroString(data + off, Length() - off, 1))); + return rv; + } + +RecordVal* IPv6_Fragment::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_fragment_type, "ip6_fragment")); + 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(4, new Val(ntohs(frag->ip6f_offlg) & 0x0001, TYPE_BOOL)); + rv->Assign(5, new Val(ntohl(frag->ip6f_ident), TYPE_COUNT)); + return rv; + } + +RecordVal* IPv6_AH::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_ah_type, "ip6_ah")); + rv->Assign(0, new Val(((ip6_ext*)data)->ip6e_nxt, TYPE_COUNT)); + rv->Assign(1, new Val(((ip6_ext*)data)->ip6e_len, TYPE_COUNT)); + rv->Assign(2, new Val(ntohs(((uint16*)data)[1]), TYPE_COUNT)); + rv->Assign(3, new Val(ntohl(((uint32*)data)[1]), TYPE_COUNT)); + rv->Assign(4, new Val(ntohl(((uint32*)data)[2]), TYPE_COUNT)); + uint16 off = 3 * sizeof(uint32); + rv->Assign(5, new StringVal(new BroString(data + off, Length() - off, 1))); + return rv; + } + +RecordVal* IPv6_ESP::BuildRecordVal() const + { + RecordVal* rv = new RecordVal(hdrType(ip6_esp_type, "ip6_esp")); + const uint32* esp = (const uint32*)data; + rv->Assign(0, new Val(ntohl(esp[0]), TYPE_COUNT)); + rv->Assign(1, new Val(ntohl(esp[1]), TYPE_COUNT)); + return rv; + } + +RecordVal* IP_Hdr::BuildRecordVal() const + { + RecordVal* rval = 0; + + if ( ! ip_hdr_type ) + { + ip_hdr_type = internal_type("ip_hdr")->AsRecordType(); + ip6_hdr_type = internal_type("ip6_hdr")->AsRecordType(); + ip6_hdr_chain_type = internal_type("ip6_hdr_chain")->AsRecordType(); + ip6_hopopts_type = internal_type("ip6_hopopts")->AsRecordType(); + ip6_dstopts_type = internal_type("ip6_dstopts")->AsRecordType(); + ip6_routing_type = internal_type("ip6_routing")->AsRecordType(); + ip6_fragment_type = internal_type("ip6_fragment")->AsRecordType(); + ip6_ah_type = internal_type("ip6_ah")->AsRecordType(); + ip6_esp_type = internal_type("ip6_esp")->AsRecordType(); + } + + if ( ip4 ) + { + rval = new RecordVal(ip_hdr_type); + rval->Assign(0, new Val(ip4->ip_hl * 4, TYPE_COUNT)); + rval->Assign(1, new Val(ip4->ip_tos, TYPE_COUNT)); + rval->Assign(2, new Val(ntohs(ip4->ip_len), TYPE_COUNT)); + rval->Assign(3, new Val(ntohs(ip4->ip_id), TYPE_COUNT)); + rval->Assign(4, new Val(ip4->ip_ttl, TYPE_COUNT)); + rval->Assign(5, new Val(ip4->ip_p, TYPE_COUNT)); + rval->Assign(6, new AddrVal(ip4->ip_src.s_addr)); + rval->Assign(7, new AddrVal(ip4->ip_dst.s_addr)); + } + else + { + rval = new RecordVal(ip6_hdr_chain_type); + + VectorVal* hopopts = new VectorVal(new VectorType(ip6_hopopts_type->Ref())); + VectorVal* dstopts = new VectorVal(new VectorType(ip6_dstopts_type->Ref())); + VectorVal* routing = new VectorVal(new VectorType(ip6_routing_type->Ref())); + VectorVal* fragment = new VectorVal(new VectorType(ip6_fragment_type->Ref())); + VectorVal* ah = new VectorVal(new VectorType(ip6_ah_type->Ref())); + VectorVal* esp = new VectorVal(new VectorType(ip6_esp_type->Ref())); + VectorVal* order = new VectorVal(new VectorType(base_type(TYPE_COUNT))); + + for ( size_t i = 1; i < ip6_hdrs->Size(); ++i ) + { + RecordVal* v = ((*ip6_hdrs)[i])->BuildRecordVal(); + uint8 type = ((*ip6_hdrs)[i])->Type(); + switch (type) { + case IPPROTO_HOPOPTS: + hopopts->Assign(hopopts->Size(), v, 0); + break; + case IPPROTO_ROUTING: + routing->Assign(routing->Size(), v, 0); + break; + case IPPROTO_DSTOPTS: + dstopts->Assign(dstopts->Size(), v, 0); + break; + case IPPROTO_FRAGMENT: + fragment->Assign(fragment->Size(), v, 0); + break; + case IPPROTO_AH: + ah->Assign(ah->Size(), v, 0); + break; + case IPPROTO_ESP: + esp->Assign(esp->Size(), v, 0); + break; + case IPPROTO_IPV6: + default: + reporter->InternalError("pkt_hdr assigned bad header %d", type); + break; + } + order->Assign(i, new Val(type, TYPE_COUNT), 0); + } + + rval->Assign(0, ((*ip6_hdrs)[0])->BuildRecordVal()); + rval->Assign(1, hopopts); + rval->Assign(2, dstopts); + rval->Assign(3, routing); + rval->Assign(4, fragment); + rval->Assign(5, ah); + rval->Assign(6, esp); + rval->Assign(7, order); + } + + return rval; + } + +static inline IPv6_Hdr* getIPv6Header(uint8 type, const u_char* d) + { + switch (type) { + case IPPROTO_IPV6: + return new IPv6_Hdr(d); + case IPPROTO_HOPOPTS: + return new IPv6_HopOpts(d); + case IPPROTO_ROUTING: + return new IPv6_Routing(d); + case IPPROTO_DSTOPTS: + return new IPv6_DstOpts(d); + case IPPROTO_FRAGMENT: + return new IPv6_Fragment(d); + case IPPROTO_AH: + return new IPv6_AH(d); + case IPPROTO_ESP: + return new IPv6_ESP(d); + default: + // should never get here if calls are protected by isIPv6ExtHeader() + reporter->InternalError("Unknown IPv6 header type: %d", type); + break; + } + // can't be reached + assert(false); + return 0; + } + +static inline bool isIPv6ExtHeader(uint8 type) + { + switch (type) { + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + case IPPROTO_FRAGMENT: + case IPPROTO_AH: + case IPPROTO_ESP: + return true; + default: + return false; + } + } + +IPv6_Hdr_Chain::IPv6_Hdr_Chain(const struct ip6_hdr* ip6) + { + length = 0; + uint8 current_type, next_type; + next_type = IPPROTO_IPV6; + const u_char* hdrs = (const u_char*) ip6; + + do + { + current_type = next_type; + chain.push_back(getIPv6Header(current_type, hdrs)); + next_type = chain[chain.size()-1]->NextHdr(); + uint16 len = chain[chain.size()-1]->Length(); + hdrs += len; + length += len; + } while ( current_type != IPPROTO_FRAGMENT && + current_type != IPPROTO_ESP && + isIPv6ExtHeader(next_type) ); + } diff --git a/src/IP.h b/src/IP.h index 36e8634912..09640f47b9 100644 --- a/src/IP.h +++ b/src/IP.h @@ -4,8 +4,139 @@ #define ip_h #include "config.h" +#include "net_util.h" #include "IPAddr.h" -#include +#include "Reporter.h" +#include "Val.h" +#include "Type.h" +#include +#include +#include +#include + +/** + * Base class for IPv6 header/extensions. + */ +class IPv6_Hdr { +public: + IPv6_Hdr() : type(0), data(0) {} + + /** + * Construct the main IPv6 header. + */ + IPv6_Hdr(const u_char* d) : type(IPPROTO_IPV6), data(d) {} + + /** + * Construct an IPv6 header or extension header from assigned type number. + */ + IPv6_Hdr(uint8 t, const u_char* d) : type(t), data(d) {} + + virtual ~IPv6_Hdr() {} + + /** + * Returns the assigned IPv6 extension header type number of the header + * that immediately follows this one. + */ + virtual uint8 NextHdr() const { return ((ip6_hdr*)data)->ip6_nxt; } + + /** + * Returns the length of the header in bytes. + */ + virtual uint16 Length() const { return 40; } + + /** + * Returns the RFC 1700 assigned number indicating the header type. + */ + uint8 Type() const { return type; } + + /** + * Returns the script-layer record representation of the header. + */ + virtual RecordVal* BuildRecordVal() const; + +protected: + uint8 type; + const u_char* data; +}; + +class IPv6_HopOpts : public IPv6_Hdr { +public: + IPv6_HopOpts(const u_char* d) : IPv6_Hdr(IPPROTO_HOPOPTS, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_DstOpts : public IPv6_Hdr { +public: + IPv6_DstOpts(const u_char* d) : IPv6_Hdr(IPPROTO_DSTOPTS, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_Routing : public IPv6_Hdr { +public: + IPv6_Routing(const u_char* d) : IPv6_Hdr(IPPROTO_ROUTING, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + uint16 Length() const { return 8 + 8 * ((ip6_ext*)data)->ip6e_len; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_Fragment : public IPv6_Hdr { +public: + IPv6_Fragment(const u_char* d) : IPv6_Hdr(IPPROTO_FRAGMENT, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + uint16 Length() const { return 8; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_AH : public IPv6_Hdr { +public: + IPv6_AH(const u_char* d) : IPv6_Hdr(IPPROTO_AH, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + uint16 Length() const { return 8 + 4 * ((ip6_ext*)data)->ip6e_len; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_ESP : public IPv6_Hdr { +public: + IPv6_ESP(const u_char* d) : IPv6_Hdr(IPPROTO_ESP, d) {} + uint8 NextHdr() const { return ((ip6_ext*)data)->ip6e_nxt; } + // encrypted payload begins after 8 bytes + uint16 Length() const { return 8; } + RecordVal* BuildRecordVal() const; +}; + +class IPv6_Hdr_Chain { +public: + /** + * Initializes the header chain from an IPv6 header structure. + */ + IPv6_Hdr_Chain(const struct ip6_hdr* ip6); + + ~IPv6_Hdr_Chain() + { for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; } + + /** + * Returns the number of headers in the chain. + */ + size_t Size() const { return chain.size(); } + + /** + * Returns the sum of the length of all headers in the chain in bytes. + */ + uint16 TotalLength() const { return length; } + + /** + * Accesses the header at the given location in the chain. + */ + const IPv6_Hdr* operator[](const size_t i) const { return chain[i]; } + +protected: + vector chain; + uint16 length; // The summation of all header lengths in the chain in bytes. +}; class IP_Hdr { public: @@ -17,10 +148,12 @@ public: IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del) : ip4(0), ip6(arg_ip6), del(arg_del) { + ip6_hdrs = new IPv6_Hdr_Chain(ip6); } ~IP_Hdr() { + if ( ip6 ) delete ip6_hdrs; if ( del ) { if ( ip4 ) @@ -30,23 +163,23 @@ public: } } + //TODO: audit usages of this for correct IPv6 support or IPv4 assumptions const struct ip* IP4_Hdr() const { return ip4; } + const struct ip6_hdr* IP6_Hdr() const { return ip6; } IPAddr SrcAddr() const { return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); } + IPAddr DstAddr() const { return ip4 ? IPAddr(ip4->ip_dst) : IPAddr(ip6->ip6_dst); } - //TODO: needs adapting/replacement for IPv6 support - uint16 ID4() const { return ip4 ? ip4->ip_id : 0; } - const u_char* Payload() const { if ( ip4 ) return ((const u_char*) ip4) + ip4->ip_hl * 4; else - return ((const u_char*) ip6) + 40; + return ((const u_char*) ip6) + ip6_hdrs->TotalLength(); } uint16 PayloadLen() const @@ -54,33 +187,60 @@ public: if ( ip4 ) return ntohs(ip4->ip_len) - ip4->ip_hl * 4; else - return ntohs(ip6->ip6_plen); + return ntohs(ip6->ip6_plen) - ip6_hdrs->TotalLength(); } uint16 TotalLen() const - { - if ( ip4 ) - return ntohs(ip4->ip_len); - else - return ntohs(ip6->ip6_plen) + 40; - } + { return ip4 ? ntohs(ip4->ip_len) : ntohs(ip6->ip6_plen) + 40; } + + uint16 HdrLen() const + { return ip4 ? ip4->ip_hl * 4 : ip6_hdrs->TotalLength(); } + + uint8 LastHeader() const + { return ip4 ? IPPROTO_RAW : + ((*ip6_hdrs)[ip6_hdrs->Size()-1])->Type(); } - uint16 HdrLen() const { return ip4 ? ip4->ip_hl * 4 : 40; } unsigned char NextProto() const - { return ip4 ? ip4->ip_p : ip6->ip6_nxt; } + { return ip4 ? ip4->ip_p : + ((*ip6_hdrs)[ip6_hdrs->Size()-1])->NextHdr(); } + 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; } + + //TODO: check for IPv6 Fragment ext. header + uint16 FragOffset() const + { return ip4 ? (ntohs(ip4->ip_off) & 0x1fff) * 8 : 0; } + + //TODO: check for IPv6 Fragment ext. header uint16 FragField() const - { return ntohs(ip4 ? ip4->ip_off : 0); } + { return ip4 ? ntohs(ip4->ip_off) : 0; } + + //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; } + + // IPv6 has no "Don't Fragment" flag. int DF() const - { return ip4 ? ((ntohs(ip4->ip_off) & IP_DF) != 0) : 0; } - uint16 IP_ID() const - { return ip4 ? (ntohs(ip4->ip_id)) : 0; } + { return ip4 ? ((ntohs(ip4->ip_off) & 0x4000) != 0) : 0; } + + size_t NumHeaders() const + { return ip4 ? 1 : ip6_hdrs->Size(); } + + RecordVal* BuildRecordVal() const; private: const struct ip* ip4; const struct ip6_hdr* ip6; bool del; + IPv6_Hdr_Chain* ip6_hdrs; }; #endif diff --git a/src/PacketSort.cc b/src/PacketSort.cc index d0e04a37ea..7bfdaba9a0 100644 --- a/src/PacketSort.cc +++ b/src/PacketSort.cc @@ -33,7 +33,7 @@ PacketSortElement::PacketSortElement(PktSrc* arg_src, if ( ip_hdr->NextProto() == IPPROTO_TCP && // Note: can't sort fragmented packets - (ip_hdr->FragField() & 0x3fff) == 0 ) + ( ! ip_hdr->IsFragment() ) ) { tcp_offset = hdr_size + ip_hdr->HdrLen(); if ( caplen >= tcp_offset + sizeof(struct tcphdr) ) diff --git a/src/Sessions.cc b/src/Sessions.cc index 04b877dfe0..e8cece9e46 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -332,7 +332,8 @@ void NetSessions::NextPacketSecondary(double /* t */, const struct pcap_pkthdr* StringVal* cmd_val = new StringVal(sp->Event()->Filter()); args->append(cmd_val); - args->append(BuildHeader(ip)); + IP_Hdr ip_hdr(ip, false); + args->append(BuildHeader(&ip_hdr)); // ### Need to queue event here. try { @@ -400,18 +401,6 @@ int NetSessions::CheckConnectionTag(Connection* conn) return 1; } - -static bool looks_like_IPv4_packet(int len, const struct ip* ip_hdr) - { - if ( (unsigned int) len < sizeof(struct ip) ) - return false; - - if ( ip_hdr->ip_v == 4 && ntohs(ip_hdr->ip_len) == len ) - return true; - else - return false; - } - void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, const IP_Hdr* ip_hdr, const u_char* const pkt, int hdr_size) @@ -441,18 +430,9 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) ) return; - int proto = ip_hdr->NextProto(); - if ( proto != IPPROTO_TCP && proto != IPPROTO_UDP && - proto != IPPROTO_ICMP ) - { - dump_this_packet = 1; - return; - } - FragReassembler* f = 0; - uint32 frag_field = ip_hdr->FragField(); - if ( (frag_field & 0x3fff) != 0 ) + if ( ip_hdr->IsFragment() ) { dump_this_packet = 1; // always record fragments @@ -463,12 +443,12 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, // Don't try to reassemble, that's doomed. // Discard all except the first fragment (which // is useful in analyzing header-only traces) - if ( (frag_field & 0x1fff) != 0 ) + if ( ip_hdr->FragOffset() != 0 ) return; } else { - f = NextFragment(t, ip_hdr, pkt + hdr_size, frag_field); + f = NextFragment(t, ip_hdr, pkt + hdr_size); const IP_Hdr* ih = f->ReassembledPkt(); if ( ! ih ) // It didn't reassemble into anything yet. @@ -485,21 +465,24 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, len -= ip_hdr_len; // remove IP header caplen -= ip_hdr_len; - uint32 min_hdr_len = (proto == IPPROTO_TCP) ? sizeof(struct tcphdr) : - (proto == IPPROTO_UDP ? sizeof(struct udphdr) : ICMP_MINLEN); - - if ( len < min_hdr_len ) + if ( ip_hdr->LastHeader() == IPPROTO_ESP ) { - Weird("truncated_header", hdr, pkt); - if ( f ) - Remove(f); // ### + if ( esp_packet ) + { + val_list* vl = new val_list(); + vl->append(ip_hdr->BuildRecordVal()); + mgr.QueueEvent(esp_packet, vl); + } + Remove(f); + // Can't do more since upper-layer payloads are going to be encrypted return; } - if ( caplen < min_hdr_len ) + + int proto = ip_hdr->NextProto(); + + if ( CheckHeaderTrunc(proto, len, caplen, hdr, pkt) ) { - Weird("internally_truncated_header", hdr, pkt); - if ( f ) - Remove(f); // ### + Remove(f); return; } @@ -549,6 +532,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, default: Weird(fmt("unknown_protocol %d", proto), hdr, pkt); + Remove(f); return; } @@ -574,6 +558,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, if ( consistent < 0 ) { delete h; + Remove(f); return; } @@ -592,10 +577,11 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } if ( ! conn ) + { delete h; - - if ( ! conn ) + Remove(f); return; + } int record_packet = 1; // whether to record the packet at all int record_content = 1; // whether to record its data @@ -603,8 +589,17 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, int is_orig = (id.src_addr == conn->OrigAddr()) && (id.src_port == conn->OrigPort()); - if ( new_packet && ip4 ) - conn->Event(new_packet, 0, BuildHeader(ip4)); + Val* pkt_hdr_val = 0; + + if ( ipv6_ext_headers && ip_hdr->NumHeaders() > 1 ) + { + pkt_hdr_val = BuildHeader(ip_hdr); + conn->Event(new_packet, 0, pkt_hdr_val); + } + + if ( new_packet ) + conn->Event(new_packet, 0, + pkt_hdr_val ? pkt_hdr_val->Ref() : BuildHeader(ip_hdr)); conn->NextPacket(t, is_orig, ip_hdr, len, caplen, data, record_packet, record_content, @@ -614,7 +609,7 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, { // Above we already recorded the fragment in its entirety. f->DeleteTimer(); - Remove(f); // ### + Remove(f); } else if ( record_packet ) @@ -630,10 +625,39 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } } -Val* NetSessions::BuildHeader(const struct ip* ip) +bool NetSessions::CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, + const struct pcap_pkthdr* h, const u_char* p) + { + uint32 min_hdr_len = 0; + switch ( proto ) { + case IPPROTO_TCP: + min_hdr_len = sizeof(struct tcphdr); + break; + case IPPROTO_UDP: + min_hdr_len = sizeof(struct udphdr); + break; + case IPPROTO_ICMP: + default: + min_hdr_len = ICMP_MINLEN; + break; + } + if ( len < min_hdr_len ) + { + Weird("truncated_header", h, p); + return true; + } + if ( caplen < min_hdr_len ) + { + Weird("internally_truncated_header", h, p); + return true; + } + return false; + } + + +Val* NetSessions::BuildHeader(const IP_Hdr* ip) { static RecordType* pkt_hdr_type = 0; - static RecordType* ip_hdr_type = 0; static RecordType* tcp_hdr_type = 0; static RecordType* udp_hdr_type = 0; static RecordType* icmp_hdr_type; @@ -641,7 +665,6 @@ Val* NetSessions::BuildHeader(const struct ip* ip) if ( ! pkt_hdr_type ) { pkt_hdr_type = internal_type("pkt_hdr")->AsRecordType(); - ip_hdr_type = internal_type("ip_hdr")->AsRecordType(); tcp_hdr_type = internal_type("tcp_hdr")->AsRecordType(); udp_hdr_type = internal_type("udp_hdr")->AsRecordType(); icmp_hdr_type = internal_type("icmp_hdr")->AsRecordType(); @@ -649,26 +672,15 @@ Val* NetSessions::BuildHeader(const struct ip* ip) RecordVal* pkt_hdr = new RecordVal(pkt_hdr_type); - RecordVal* ip_hdr = new RecordVal(ip_hdr_type); - - int ip_hdr_len = ip->ip_hl * 4; - int ip_pkt_len = ntohs(ip->ip_len); - - ip_hdr->Assign(0, new Val(ip->ip_hl * 4, TYPE_COUNT)); - ip_hdr->Assign(1, new Val(ip->ip_tos, TYPE_COUNT)); - ip_hdr->Assign(2, new Val(ip_pkt_len, TYPE_COUNT)); - ip_hdr->Assign(3, new Val(ntohs(ip->ip_id), TYPE_COUNT)); - ip_hdr->Assign(4, new Val(ip->ip_ttl, TYPE_COUNT)); - ip_hdr->Assign(5, new Val(ip->ip_p, TYPE_COUNT)); - ip_hdr->Assign(6, new AddrVal(ip->ip_src.s_addr)); - ip_hdr->Assign(7, new AddrVal(ip->ip_dst.s_addr)); - - pkt_hdr->Assign(0, ip_hdr); + if ( ip->IP4_Hdr() ) + pkt_hdr->Assign(0, ip->BuildRecordVal()); + else + pkt_hdr->Assign(1, ip->BuildRecordVal()); // L4 header. - const u_char* data = ((const u_char*) ip) + ip_hdr_len; + const u_char* data = ip->Payload(); - int proto = ip->ip_p; + int proto = ip->NextProto(); switch ( proto ) { case IPPROTO_TCP: { @@ -676,7 +688,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip) RecordVal* tcp_hdr = new RecordVal(tcp_hdr_type); int tcp_hdr_len = tp->th_off * 4; - int data_len = ip_pkt_len - ip_hdr_len - tcp_hdr_len; + int data_len = ip->PayloadLen() - tcp_hdr_len; tcp_hdr->Assign(0, new PortVal(ntohs(tp->th_sport), TRANSPORT_TCP)); tcp_hdr->Assign(1, new PortVal(ntohs(tp->th_dport), TRANSPORT_TCP)); @@ -687,7 +699,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip) tcp_hdr->Assign(6, new Val(tp->th_flags, TYPE_COUNT)); tcp_hdr->Assign(7, new Val(ntohs(tp->th_win), TYPE_COUNT)); - pkt_hdr->Assign(1, tcp_hdr); + pkt_hdr->Assign(2, tcp_hdr); break; } @@ -700,7 +712,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip) udp_hdr->Assign(1, new PortVal(ntohs(up->uh_dport), TRANSPORT_UDP)); udp_hdr->Assign(2, new Val(ntohs(up->uh_ulen), TYPE_COUNT)); - pkt_hdr->Assign(2, udp_hdr); + pkt_hdr->Assign(3, udp_hdr); break; } @@ -711,7 +723,7 @@ Val* NetSessions::BuildHeader(const struct ip* ip) icmp_hdr->Assign(0, new Val(icmpp->icmp_type, TYPE_COUNT)); - pkt_hdr->Assign(3, icmp_hdr); + pkt_hdr->Assign(4, icmp_hdr); break; } @@ -725,9 +737,9 @@ Val* NetSessions::BuildHeader(const struct ip* ip) } FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip, - const u_char* pkt, uint32 frag_field) + const u_char* pkt) { - uint32 frag_id = ntohs(ip->ID4()); // we actually could skip conv. + uint32 frag_id = ip->ID(); ListVal* key = new ListVal(TYPE_ANY); key->Append(new AddrVal(ip->SrcAddr())); @@ -741,7 +753,7 @@ FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip, FragReassembler* f = fragments.Lookup(h); if ( ! f ) { - f = new FragReassembler(this, ip, pkt, frag_field, h, t); + f = new FragReassembler(this, ip, pkt, h, t); fragments.Insert(h, f); Unref(key); return f; @@ -750,7 +762,7 @@ FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip, delete h; Unref(key); - f->AddFragment(t, ip, pkt, frag_field); + f->AddFragment(t, ip, pkt); return f; } @@ -909,6 +921,7 @@ void NetSessions::Remove(Connection* c) void NetSessions::Remove(FragReassembler* f) { + if ( ! f ) return; HashKey* k = f->Key(); if ( ! k ) reporter->InternalError("fragment block not in dictionary"); diff --git a/src/Sessions.h b/src/Sessions.h index 0a6338899b..ac5fcacfb5 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -79,7 +79,7 @@ public: // Returns a reassembled packet, or nil if there are still // some missing fragments. FragReassembler* NextFragment(double t, const IP_Hdr* ip, - const u_char* pkt, uint32 frag_field); + const u_char* pkt); int Get_OS_From_SYN(struct os_type* retval, uint16 tot, uint8 DF_flag, uint8 TTL, uint16 WSS, @@ -193,7 +193,13 @@ protected: // Builds a record encapsulating a packet. This should be more // general, including the equivalent of a union of tcp/udp/icmp // headers . - Val* BuildHeader(const struct ip* ip); + Val* BuildHeader(const IP_Hdr* ip); + + // For a given protocol, checks whether the header's length as derived + // from lower-level headers or the length actually captured is less + // than that protocol's minimum header size. + bool CheckHeaderTrunc(int proto, uint32 len, uint32 caplen, + const struct pcap_pkthdr* hdr, const u_char* pkt); CompositeHash* ch; PDict(Connection) tcp_conns; diff --git a/src/TCP.cc b/src/TCP.cc index 3315db79f3..57e4449bf8 100644 --- a/src/TCP.cc +++ b/src/TCP.cc @@ -1203,7 +1203,7 @@ RecordVal* TCP_Analyzer::BuildOSVal(int is_orig, const IP_Hdr* ip, if ( ip->HdrLen() > 20 ) quirks |= QUIRK_IPOPT; - if ( ip->IP_ID() == 0 ) + if ( ip->ID() == 0 ) quirks |= QUIRK_ZEROID; if ( tcp->th_seq == 0 ) @@ -1942,11 +1942,11 @@ int TCPStats_Endpoint::DataSent(double /* t */, int seq, int len, int caplen, { if ( ++num_pkts == 1 ) { // First packet. - last_id = ntohs(ip->ID4()); + last_id = ip->ID(); return 0; } - int id = ntohs(ip->ID4()); + int id = ip->ID(); if ( id == last_id ) { diff --git a/src/event.bif b/src/event.bif index 1423750f29..1745139f11 100644 --- a/src/event.bif +++ b/src/event.bif @@ -454,11 +454,30 @@ event expected_connection_seen%(c: connection, a: count%); ## ## c: The connection the packet is part of. ## -## p: Informattion from the header of the packet that triggered the event. +## p: Information from the header of the packet that triggered the event. ## ## .. bro:see:: tcp_packet packet_contents event new_packet%(c: connection, p: pkt_hdr%); +## Generated for every IPv6 packet that contains extension headers. +## This is potentially an expensive event to handle if analysiing IPv6 traffic +## that happens to utilize extension headers frequently. +## +## c: The connection the packet is part of. +## +## p: Information from the header of the packet that triggered the event. +## +## .. bro:see:: new_packet tcp_packet packet_contents esp_packet +event ipv6_ext_headers%(c: connection, p: pkt_hdr%); + +## Generated for any packets using the IPv6 Encapsulating Security Payload (ESP) +## extension header. +## +## p: Information from the header of the packet that triggered the event. +## +## .. bro:see:: new_packet tcp_packet ipv6_ext_headers +event esp_packet%(p: pkt_hdr%); + ## Generated for every packet that has non-empty transport-layer payload. This is a ## very low-level and expensive event that should be avoided when at all possible. ## It's usually infeasible to handle when processing even medium volumes of