From b52436a53bfc849afbab7b1ae9b1015736df230f Mon Sep 17 00:00:00 2001 From: Jon Siwek Date: Tue, 5 Jun 2012 12:23:16 -0500 Subject: [PATCH] Refactor some of the NetSessions routines that recurse on IP packets. Separating the IP packet validation/parsing from the recursive call to DoNextPacket to make it easier for analyzers to get access to the inner IP_Hdr. --- src/Sessions.cc | 102 ++++++++++++++++++++++++----------------- src/Sessions.h | 33 ++++++++----- src/Teredo.cc | 24 ++++++---- src/ayiya-analyzer.pac | 22 +++++---- 4 files changed, 110 insertions(+), 71 deletions(-) diff --git a/src/Sessions.cc b/src/Sessions.cc index 9738f380d7..4e81ba1661 100644 --- a/src/Sessions.cc +++ b/src/Sessions.cc @@ -538,6 +538,23 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, return; } + // Check for a valid inner packet first. + IP_Hdr* inner = 0; + int result = ParseIPPacket(caplen, data, proto, inner); + + if ( result < 0 ) + reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), + "truncated_inner_IP"); + else if ( result > 0 ) + reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), + "inner_IP_payload_mismatch"); + + if ( result != 0 ) + { + Remove(f); + return; + } + Encapsulation* outer = new Encapsulation(encapsulation); // Look up to see if we've already seen this IP tunnel, identified @@ -561,14 +578,9 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, else outer->Add(it->second); - int result = DoNextInnerPacket(t, hdr, caplen, data, proto, outer); - if ( result < 0 ) - reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), - "truncated_inner_IP"); - else if ( result > 0 ) - reporter->Weird(ip_hdr->SrcAddr(), ip_hdr->DstAddr(), - "inner_IP_payload_mismatch"); + DoNextInnerPacket(t, hdr, inner, outer); + delete inner; delete outer; Remove(f); return; @@ -576,12 +588,11 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, case IPPROTO_NONE: { - if ( encapsulation && - encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) - { - // TODO: raise bubble packet event - } - else + // If the packet is encapsulated in Teredo, then it was a bubble and + // the Teredo analyzer may have raised an event for that, else we're + // not sure the reason for the No Next header in the packet. + if ( ! ( encapsulation && + encapsulation->LastType() == BifEnum::Tunnel::TEREDO ) ) Weird("ipv6_no_next", hdr, pkt); Remove(f); @@ -688,43 +699,50 @@ void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr, } } -int NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, - int caplen, const u_char* const pkt, int proto, - const Encapsulation* outer) +void NetSessions::DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, + const IP_Hdr* inner, const Encapsulation* outer) { - IP_Hdr* inner_ip = 0; - - if ( proto == IPPROTO_IPV6 ) - { - if ( caplen < (int)sizeof(struct ip6_hdr) ) - return -1; - inner_ip = new IP_Hdr((const struct ip6_hdr*) pkt, false, caplen); - } - else if ( proto == IPPROTO_IPV4 ) - { - if ( caplen < (int)sizeof(struct ip) ) - return -1; - inner_ip = new IP_Hdr((const struct ip*) pkt, false); - } - else - reporter->InternalError("Bad IP protocol version in DoNextInnerPacket"); - - if ( (uint32)caplen != inner_ip->TotalLen() ) - { - delete inner_ip; - return (uint32)caplen < inner_ip->TotalLen() ? -1 : 1; - } - struct pcap_pkthdr fake_hdr; - fake_hdr.caplen = fake_hdr.len = caplen; + fake_hdr.caplen = fake_hdr.len = inner->TotalLen(); if ( hdr ) fake_hdr.ts = hdr->ts; else fake_hdr.ts.tv_sec = fake_hdr.ts.tv_usec = 0; - DoNextPacket(t, &fake_hdr, inner_ip, pkt, 0, outer); + const u_char* pkt = 0; + if ( inner->IP4_Hdr() ) + pkt = (const u_char*) inner->IP4_Hdr(); + else + pkt = (const u_char*) inner->IP6_Hdr(); + + DoNextPacket(t, &fake_hdr, inner, pkt, 0, outer); + } + +int NetSessions::ParseIPPacket(int caplen, const u_char* const pkt, int proto, + IP_Hdr*& inner) + { + if ( proto == IPPROTO_IPV6 ) + { + if ( caplen < (int)sizeof(struct ip6_hdr) ) + return -1; + inner = new IP_Hdr((const struct ip6_hdr*) pkt, false, caplen); + } + else if ( proto == IPPROTO_IPV4 ) + { + if ( caplen < (int)sizeof(struct ip) ) + return -1; + inner = new IP_Hdr((const struct ip*) pkt, false); + } + else + reporter->InternalError("Bad IP protocol version in DoNextInnerPacket"); + + if ( (uint32)caplen != inner->TotalLen() ) + { + delete inner; + inner = 0; + return (uint32)caplen < inner->TotalLen() ? -1 : 1; + } - delete inner_ip; return 0; } diff --git a/src/Sessions.h b/src/Sessions.h index c374dcb667..9273a02787 100644 --- a/src/Sessions.h +++ b/src/Sessions.h @@ -139,28 +139,37 @@ public: int hdr_size, const Encapsulation* encapsulation); /** - * Wrapper that recurses on DoNextPacket for encapsulated IP packets, if - * they appear to be valid based on whether \a pkt is long enough to be an - * IP header and also that the payload length field of that header matches - * matches the actual length of \a pkt given by \a caplen. + * Wrapper that recurses on DoNextPacket for encapsulated IP packets. * * @param t Network time. * @param hdr If the outer pcap header is available, this pointer can be set * so that the fake pcap header passed to DoNextPacket will use * the same timeval. The caplen and len fields of the fake pcap - * header are always set to \a caplen. + * header are always set to the TotalLength() of \a inner. + * @param outer The encapsulation information for the inner IP packet. + */ + void DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, + const IP_Hdr* inner, const Encapsulation* outer); + + /** + * Returns a wrapper IP_Hdr object if \a pkt appears to be a valid IPv4 + * or IPv6 header based on whether it's long enough to contain such a header + * and also that the payload length field of that header matches the actual + * length of \a pkt given by \a caplen. + * * @param caplen The length of \a pkt in bytes. * @param pkt The inner IP packet data. * @param proto Either IPPROTO_IPV6 or IPPROTO_IPV4 to indicate which IP * protocol \a pkt corresponds to. - * @param outer_encap The encapsulation information for the inner IP packet. - * @return 0 If the inner IP packet was valid and passed to DoNextPacket, - * else -1 if the \a caplen was greater than the supposed IP - * packet's payload length field or 1 if \a caplen was less than - * the supposed IP packet's payload length. + * @param inner The inner IP packet wrapper pointer to be allocated/assigned + * if \a pkt looks like a valid IP packet. + * @return 0 If the inner IP packet appeared valid in which case the caller + * is responsible for deallocating \a inner, else -1 if \a caplen + * is greater than the supposed IP packet's payload length field or + * 1 if \a caplen is less than the supposed packet's payload length. */ - int DoNextInnerPacket(double t, const struct pcap_pkthdr* hdr, int caplen, - const u_char* const pkt, int proto, const Encapsulation* outer); + int ParseIPPacket(int caplen, const u_char* const pkt, int proto, + IP_Hdr*& inner); unsigned int ConnectionMemoryUsage(); unsigned int ConnectionMemoryUsageConnVals(); diff --git a/src/Teredo.cc b/src/Teredo.cc index c7131866f9..92cdc7f64f 100644 --- a/src/Teredo.cc +++ b/src/Teredo.cc @@ -109,20 +109,26 @@ void Teredo_Analyzer::DeliverPacket(int len, const u_char* data, bool orig, return; } - // TODO: raise Teredo-specific events + IP_Hdr* inner = 0; + int rslt = sessions->ParseIPPacket(len, te.InnerIP(), IPPROTO_IPV6, inner); + + if ( rslt == 0 ) + ProtocolConfirmation(); + else if ( rslt < 0 ) + ProtocolViolation("Truncated Teredo", (const char*) data, len); + else + ProtocolViolation("Teredo payload length", (const char*) data, len); + + if ( rslt != 0 ) return; + + // TODO: raise Teredo-specific events for bubbles, origin/authentication Encapsulation* outer = new Encapsulation(e); EncapsulatingConn ec(Conn(), BifEnum::Tunnel::TEREDO); outer->Add(ec); - int result = sessions->DoNextInnerPacket(network_time, 0, len, te.InnerIP(), - IPPROTO_IPV6, outer); - if ( result == 0 ) - ProtocolConfirmation(); - else if ( result < 0 ) - ProtocolViolation("Truncated Teredo", (const char*) data, len); - else - ProtocolViolation("Teredo payload length", (const char*) data, len); + sessions->DoNextInnerPacket(network_time, 0, inner, outer); + delete inner; delete outer; } diff --git a/src/ayiya-analyzer.pac b/src/ayiya-analyzer.pac index 361d5f8c66..a0b9de5926 100644 --- a/src/ayiya-analyzer.pac +++ b/src/ayiya-analyzer.pac @@ -51,14 +51,11 @@ flow AYIYA_Flow ${pdu.packet}.length()); return false; } - - Encapsulation* outer = new Encapsulation(e); - EncapsulatingConn ec(c, BifEnum::Tunnel::AYIYA); - outer->Add(ec); - - int result = sessions->DoNextInnerPacket(network_time(), 0, - ${pdu.packet}.length(), ${pdu.packet}.data(), - ${pdu.next_header}, outer); + + IP_Hdr* inner = 0; + int result = sessions->ParseIPPacket(${pdu.packet}.length(), + ${pdu.packet}.data(), ${pdu.next_header}, inner); + if ( result == 0 ) connection()->bro_analyzer()->ProtocolConfirmation(); else if ( result < 0 ) @@ -69,7 +66,16 @@ flow AYIYA_Flow connection()->bro_analyzer()->ProtocolViolation( "AYIYA payload length", (const char*) ${pdu.packet}.data(), ${pdu.packet}.length()); + + if ( result != 0 ) return false; + + Encapsulation* outer = new Encapsulation(e); + EncapsulatingConn ec(c, BifEnum::Tunnel::AYIYA); + outer->Add(ec); + sessions->DoNextInnerPacket(network_time(), 0, inner, outer); + + delete inner; delete outer; return (result == 0) ? true : false; %}