diff --git a/src/IP.cc b/src/IP.cc index 620b294d40..cce7af152f 100644 --- a/src/IP.cc +++ b/src/IP.cc @@ -305,19 +305,12 @@ 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; + // Check for routing headers and remember final destination address. + if ( current_type == IPPROTO_ROUTING ) + ProcessRoutingHeader((const struct ip6_rthdr*) hdrs, len); - // 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"); - } + if ( current_type == IPPROTO_DSTOPTS ) + ProcessDstOpts((const struct ip6_dest*) hdrs, len); hdrs += len; length += len; @@ -326,6 +319,91 @@ void IPv6_Hdr_Chain::Init(const struct ip6_hdr* ip6, bool set_next, uint16 next) isIPv6ExtHeader(next_type) ); } +void IPv6_Hdr_Chain::ProcessRoutingHeader(const struct ip6_rthdr* r, uint16 len) + { + if ( finalDst ) + { + // RFC 2460 section 4.1 says Routing should occur at most once + reporter->Weird(SrcAddr(), DstAddr(), "multiple_routing_headers"); + return; + } + + // Last 16 bytes of header (for all known types) is the address we want + const in6_addr* addr = (const in6_addr*)(((const u_char*)r) + len - 16); + + switch ( r->ip6r_type ) { + case 0: // Defined by RFC 2460, deprecated by RFC 5095 + { + if ( r->ip6r_segleft > 0 && r->ip6r_len >= 2 ) + { + if ( r->ip6r_len % 2 == 0 ) + finalDst = new IPAddr(*addr); + else + reporter->Weird(SrcAddr(), DstAddr(), "odd_routing0_len"); + } + + // Always raise a weird since this type is deprecated + reporter->Weird(SrcAddr(), DstAddr(), "routing0_hdr"); + } + break; + + case 2: // Defined by Mobile IPv6 RFC 6275 + { + if ( r->ip6r_segleft > 0 ) + { + if ( r->ip6r_len == 2 ) + finalDst = new IPAddr(*addr); + else + reporter->Weird(SrcAddr(), DstAddr(), "bad_routing2_len"); + } + } + break; + + default: + reporter->Weird(fmt("unknown_routing_type_%d", r->ip6r_type)); + break; + } + } + +void IPv6_Hdr_Chain::ProcessDstOpts(const struct ip6_dest* d, uint16 len) + { + const u_char* data = (const u_char*) d; + len -= 2 * sizeof(uint8); + data += 2* sizeof(uint8); + + while ( len > 0 ) + { + const struct ip6_opt* opt = (const struct ip6_opt*) data; + switch ( opt->ip6o_type ) { + case 201: // Home Address Option, Mobile IPv6 RFC 6275 section 6.3 + { + if ( opt->ip6o_len == 16 ) + if ( homeAddr ) + reporter->Weird(SrcAddr(), DstAddr(), "multiple_home_addr_opts"); + else + homeAddr = new IPAddr(*((const in6_addr*)(data + 2))); + else + reporter->Weird(SrcAddr(), DstAddr(), "bad_home_addr_len"); + } + break; + + default: + break; + } + + if ( opt->ip6o_type == 0 ) + { + data += sizeof(uint8); + len -= sizeof(uint8); + } + else + { + data += 2 * sizeof(uint8) + opt->ip6o_len; + len -= 2 * sizeof(uint8) + opt->ip6o_len; + } + } + } + VectorVal* IPv6_Hdr_Chain::BuildVal() const { if ( ! ip6_ext_hdr_type ) diff --git a/src/IP.h b/src/IP.h index 7ed0968ef3..4ffb59151a 100644 --- a/src/IP.h +++ b/src/IP.h @@ -117,11 +117,15 @@ public: /** * Initializes the header chain from an IPv6 header structure. */ - IPv6_Hdr_Chain(const struct ip6_hdr* ip6) : route0_hdr_idx(0) + IPv6_Hdr_Chain(const struct ip6_hdr* ip6) : homeAddr(0), finalDst(0) { Init(ip6, false); } ~IPv6_Hdr_Chain() - { for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; } + { + for ( size_t i = 0; i < chain.size(); ++i ) delete chain[i]; + delete homeAddr; + delete finalDst; + } /** * Returns the number of headers in the chain. @@ -173,22 +177,27 @@ public: (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). + * If the chain contains a Destination Options header with a Home Address + * option as defined by Mobile IPv6 (RFC 6275), then return it, else + * return the source address in the main IPv6 header. */ - IPAddr FinalDst() const + IPAddr SrcAddr() 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); - } + if ( homeAddr ) + return IPAddr(*homeAddr); + else + return IPAddr(((const struct ip6_hdr*)(chain[0]->Data()))->ip6_src); + } + + /** + * If the chain contains a Routing header with non-zero segments left, + * then return the last address of the first such header, else return + * the destination address of the main IPv6 header. + */ + IPAddr DstAddr() const + { + if ( finalDst ) + return IPAddr(*finalDst); else return IPAddr(((const struct ip6_hdr*)(chain[0]->Data()))->ip6_dst); } @@ -208,11 +217,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) : route0_hdr_idx(0) + IPv6_Hdr_Chain(const struct ip6_hdr* ip6, uint16 next) + : homeAddr(0), finalDst(0) { Init(ip6, true, next); } void Init(const struct ip6_hdr* ip6, bool set_next, uint16 next = 0); + /** + * Process a routing header and allocate/remember the final destination + * address if it has segments left and is a valid routing header. + */ + void ProcessRoutingHeader(const struct ip6_rthdr* r, uint16 len); + + /** + * Inspect a Destination Option header's options for things we need to + * remember, such as the Home Address option from Mobile IPv6. + */ + void ProcessDstOpts(const struct ip6_dest* d, uint16 len); + vector chain; /** @@ -221,11 +243,15 @@ protected: 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). + * Home Address of the packet's source as defined by Mobile IPv6 (RFC 6275). */ - uint8 route0_hdr_idx; + IPAddr* homeAddr; + + /** + * The final destination address in chain's first Routing header that has + * non-zero segments left. + */ + IPAddr* finalDst; }; class IP_Hdr { @@ -278,20 +304,11 @@ public: const struct ip6_hdr* IP6_Hdr() const { return ip6; } - IPAddr SrcAddr() const - { return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); } - /** - * 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. + * Returns the source address held in the IP header. */ - IPAddr FinalDstAddr() const - { return ip4 ? IPAddr(ip4->ip_dst) : ip6_hdrs->FinalDst(); } + IPAddr IPheaderSrcAddr() const + { return ip4 ? IPAddr(ip4->ip_src) : IPAddr(ip6->ip6_src); } /** * Returns the destination address held in the IP header. @@ -299,6 +316,23 @@ public: IPAddr IPHeaderDstAddr() const { return ip4 ? IPAddr(ip4->ip_dst) : IPAddr(ip6->ip6_dst); } + /** + * For IPv4 or IPv6 headers that don't contain a Home Address option + * (Mobile IPv6, RFC 6275), return source address held in the IP header. + * For IPv6 headers that contain a Home Address option, return that address. + */ + IPAddr SrcAddr() const + { return ip4 ? IPAddr(ip4->ip_src) : ip6_hdrs->SrcAddr(); } + + /** + * For IPv4 or IPv6 headers that don't contain a Routing header with + * non-zero segments left, return destination address held in the IP header. + * For IPv6 headers with a Routing header that has non-zero segments left, + * return the last address in the first such Routing header. + */ + IPAddr DstAddr() const + { return ip4 ? IPAddr(ip4->ip_dst) : ip6_hdrs->DstAddr(); } + /** * Returns a pointer to the payload of the IP packet, usually an * upper-layer protocol. diff --git a/src/PacketFilter.cc b/src/PacketFilter.cc index 412bf14587..4fb3b1c8f7 100644 --- a/src/PacketFilter.cc +++ b/src/PacketFilter.cc @@ -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->FinalDstAddr(), 128); + f = (Filter*) dst_filter.Lookup(ip->DstAddr(), 128); if ( f ) return MatchFilter(*f, *ip, len, caplen); diff --git a/src/PacketSort.cc b/src/PacketSort.cc index 3fb0e9ccbf..04c525c4d1 100644 --- a/src/PacketSort.cc +++ b/src/PacketSort.cc @@ -45,7 +45,7 @@ PacketSortElement::PacketSortElement(PktSrc* arg_src, (pkt + tcp_offset); id.src_addr = ip_hdr->SrcAddr(); - id.dst_addr = ip_hdr->FinalDstAddr(); + id.dst_addr = ip_hdr->DstAddr(); id.src_port = tp->th_sport; id.dst_port = tp->th_dport; id.is_one_way = 0; diff --git a/src/Serializer.cc b/src/Serializer.cc index 6aa554cc2b..06bbf73f48 100644 --- a/src/Serializer.cc +++ b/src/Serializer.cc @@ -1105,7 +1105,7 @@ void Packet::Describe(ODesc* d) const const IP_Hdr ip = IP(); d->Add(ip.SrcAddr()); d->Add("->"); - d->Add(ip.FinalDstAddr()); + d->Add(ip.DstAddr()); } bool Packet::Serialize(SerialInfo* info) const diff --git a/src/Sessions.cc b/src/Sessions.cc index 4b5f201db5..84b57bdc62 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -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->FinalDstAddr(); + id.dst_addr = ip_hdr->DstAddr(); 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->FinalDstAddr())); + key->Append(new AddrVal(ip->DstAddr())); 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->FinalDstAddr(), name); + reporter->Weird(ip->SrcAddr(), ip->DstAddr(), name); } unsigned int NetSessions::ConnectionMemoryUsage() diff --git a/src/UDP.cc b/src/UDP.cc index fc559bf59d..d85cb39edd 100644 --- a/src/UDP.cc +++ b/src/UDP.cc @@ -217,7 +217,7 @@ bool UDP_Analyzer::ValidateChecksum(const IP_Hdr* ip, const udphdr* up, int len) sum = 0; sum = ones_complement_checksum(ip->SrcAddr(), sum); - sum = ones_complement_checksum(ip->FinalDstAddr(), sum); + sum = ones_complement_checksum(ip->DstAddr(), 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. diff --git a/src/bro.bif b/src/bro.bif index 9fbf66699e..49a57274af 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -4693,6 +4693,8 @@ function pcap_error%(%): string ## Installs a filter to drop packets from a given IP source address with ## a certain probability if none of a given set of TCP flags are set. +## Note that for IPv6 packets with a Destination options header that has +## the Home Address option, this filters out against that home address. ## ## ip: The IP address to drop. ## @@ -4795,7 +4797,7 @@ 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 +## Note that for IPv6 packets with a routing type header and non-zero ## segments left, this filters out against the final destination of the ## packet according to the routing extension header. ## diff --git a/testing/btest/Baseline/core.checksums/bad.out b/testing/btest/Baseline/core.checksums/bad.out index cd3c799277..ef83d966a3 100644 --- a/testing/btest/Baseline/core.checksums/bad.out +++ b/testing/btest/Baseline/core.checksums/bad.out @@ -5,5 +5,7 @@ 1332785210.013051 weird: bad_TCP_checksum 1332782580.798420 weird: routing0_hdr 1332782580.798420 weird: bad_UDP_checksum +1333640536.489921 weird: bad_TCP_checksum +1333640468.146461 weird: bad_UDP_checksum 1332785250.469132 weird: bad_TCP_checksum 1332781342.923813 weird: bad_UDP_checksum diff --git a/testing/btest/Baseline/core.mobile-ipv6-home-addr/output b/testing/btest/Baseline/core.mobile-ipv6-home-addr/output new file mode 100644 index 0000000000..f28997ff0b --- /dev/null +++ b/testing/btest/Baseline/core.mobile-ipv6-home-addr/output @@ -0,0 +1,2 @@ +[orig_h=2001:78:1:32::1, orig_p=30000/udp, resp_h=2001:4f8:4:7:2e0:81ff:fe52:9a6b, resp_p=13000/udp] +[ip=, ip6=[class=0, flow=0, len=36, nxt=60, hlim=64, src=2001:4f8:4:7:2e0:81ff:fe52:ffff, dst=2001:4f8:4:7:2e0:81ff:fe52:9a6b, exts=[[id=60, hopopts=, dstopts=[nxt=17, len=2, options=[[otype=1, len=2, data=\0\0], [otype=201, len=16, data= ^A\0x\0^A\02\0\0\0\0\0\0\0^A]]], routing=, fragment=, ah=, esp=]]], tcp=, udp=[sport=30000/udp, dport=13000/udp, ulen=12], icmp=] diff --git a/testing/btest/Traces/chksums/ip6-hoa-tcp-bad-chksum.pcap b/testing/btest/Traces/chksums/ip6-hoa-tcp-bad-chksum.pcap new file mode 100644 index 0000000000..3aa4bd21fa Binary files /dev/null and b/testing/btest/Traces/chksums/ip6-hoa-tcp-bad-chksum.pcap differ diff --git a/testing/btest/Traces/chksums/ip6-hoa-tcp-good-chksum.pcap b/testing/btest/Traces/chksums/ip6-hoa-tcp-good-chksum.pcap new file mode 100644 index 0000000000..a6fc9cb017 Binary files /dev/null and b/testing/btest/Traces/chksums/ip6-hoa-tcp-good-chksum.pcap differ diff --git a/testing/btest/Traces/chksums/ip6-hoa-udp-bad-chksum.pcap b/testing/btest/Traces/chksums/ip6-hoa-udp-bad-chksum.pcap new file mode 100644 index 0000000000..d2434dea80 Binary files /dev/null and b/testing/btest/Traces/chksums/ip6-hoa-udp-bad-chksum.pcap differ diff --git a/testing/btest/Traces/chksums/ip6-hoa-udp-good-chksum.pcap b/testing/btest/Traces/chksums/ip6-hoa-udp-good-chksum.pcap new file mode 100644 index 0000000000..f3e9d632c3 Binary files /dev/null and b/testing/btest/Traces/chksums/ip6-hoa-udp-good-chksum.pcap differ diff --git a/testing/btest/Traces/ipv6-mobile-hoa.trace b/testing/btest/Traces/ipv6-mobile-hoa.trace new file mode 100644 index 0000000000..f3e9d632c3 Binary files /dev/null and b/testing/btest/Traces/ipv6-mobile-hoa.trace differ diff --git a/testing/btest/core/checksums.test b/testing/btest/core/checksums.test index c01ab710af..1cf7f9c54f 100644 --- a/testing/btest/core/checksums.test +++ b/testing/btest/core/checksums.test @@ -3,12 +3,16 @@ # @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-hoa-tcp-bad-chksum.pcap >>bad.out 2>&1 +# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-hoa-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-hoa-tcp-good-chksum.pcap >>good.out 2>&1 +# @TEST-EXEC: bro -b -r $TRACES/chksums/ip6-hoa-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 diff --git a/testing/btest/core/mobile-ipv6-home-addr.test b/testing/btest/core/mobile-ipv6-home-addr.test new file mode 100644 index 0000000000..f113016568 --- /dev/null +++ b/testing/btest/core/mobile-ipv6-home-addr.test @@ -0,0 +1,10 @@ +# @TEST-EXEC: bro -b -r $TRACES/ipv6-mobile-hoa.trace %INPUT >output +# @TEST-EXEC: btest-diff output + +# Just check that the orig of the connection is the Home Address, but the +# source in the header is the actual source address. +event new_packet(c: connection, p: pkt_hdr) + { + print c$id; + print p; + }