diff options
Diffstat (limited to '')
-rw-r--r-- | libnetutil/PacketParser.cc | 1789 |
1 files changed, 1789 insertions, 0 deletions
diff --git a/libnetutil/PacketParser.cc b/libnetutil/PacketParser.cc new file mode 100644 index 0000000..01c661b --- /dev/null +++ b/libnetutil/PacketParser.cc @@ -0,0 +1,1789 @@ +/*************************************************************************** + * PacketParser.cc -- The PacketParser Class offers methods to parse * + * received network packets. Its main purpose is to facilitate the * + * conversion of raw sequences of bytes into chains of objects of the * + * PacketElement family. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * Source is provided to this software because we believe users have a + * right to know exactly what a program is going to do before they run it. + * This also allows you to audit the software for security holes. + * + * Source code also allows you to port Nmap to new platforms, fix bugs, and add + * new features. You are highly encouraged to submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ + /* This code was originally part of the Nping tool. */ + +#include "PacketParser.h" +#include <assert.h> + +#define PKTPARSERDEBUG false + +PacketParser::PacketParser() { + this->reset(); +} /* End of PacketParser constructor */ + + +PacketParser::~PacketParser() { + +} /* End of PacketParser destructor */ + + +/** Sets every attribute to its default value- */ +void PacketParser::reset() { + +} /* End of PacketParser destructor */ + + +const char *PacketParser::header_type2string(int val){ + header_type_string_t header_types[]={ + {HEADER_TYPE_IPv6_HOPOPT, "IPv6 Hop-by-Hop"}, + {HEADER_TYPE_ICMPv4,"ICMPv4"}, + {HEADER_TYPE_IGMP,"IGMP"}, + {HEADER_TYPE_IPv4,"IPv4"}, + {HEADER_TYPE_TCP,"TCP"}, + {HEADER_TYPE_EGP,"EGP"}, + {HEADER_TYPE_UDP,"UDP"}, + {HEADER_TYPE_IPv6,"IPv6"}, + {HEADER_TYPE_IPv6_ROUTE,"IPv6-Route"}, + {HEADER_TYPE_IPv6_FRAG,"IPv6-Frag"}, + {HEADER_TYPE_GRE,"GRE"}, + {HEADER_TYPE_ESP,"ESP"}, + {HEADER_TYPE_AH,"AH"}, + {HEADER_TYPE_ICMPv6,"ICMPv6"}, + {HEADER_TYPE_IPv6_NONXT,"IPv6-NoNxt"}, + {HEADER_TYPE_IPv6_OPTS,"IPv6-Opts"}, + {HEADER_TYPE_EIGRP,"EIGRP"}, + {HEADER_TYPE_ETHERNET,"Ethernet"}, + {HEADER_TYPE_L2TP,"L2TP"}, + {HEADER_TYPE_SCTP,"SCTP"}, + {HEADER_TYPE_IPv6_MOBILE,"Mobility Header"}, + {HEADER_TYPE_MPLS_IN_IP,"MPLS-in-IP"}, + {HEADER_TYPE_ARP,"ARP"}, + {HEADER_TYPE_RAW_DATA,"Raw Data"}, + {0,NULL} + }; + int i=0; + for(i=0; header_types[i].str!=NULL; i++ ){ + if((int)header_types[i].type==val) + return header_types[i].str; + } + return NULL; +} /* End of header_type2string() */ + + + +#define MAX_HEADERS_IN_PACKET 32 +pkt_type_t *PacketParser::parse_packet(const u8 *pkt, size_t pktlen, bool eth_included){ + if(PKTPARSERDEBUG)printf("%s(%p, %lu)\n", __func__, pkt, (long unsigned)pktlen); + static pkt_type_t this_packet[MAX_HEADERS_IN_PACKET+1]; /* Packet structure array */ + u8 current_header=0; /* Current array position of "this_packet" */ + const u8 *curr_pkt=pkt; /* Pointer to current part of the packet */ + size_t curr_pktlen=pktlen; /* Remaining packet length */ + int ethlen=0, arplen=0; /* Aux length variables: link layer */ + int iplen=0,ip6len=0; /* Aux length variables: network layer */ + int tcplen=0,udplen=0,icmplen=0; /* Aux length variables: transport layer */ + int exthdrlen=0; /* Aux length variables: extension headers */ + int next_layer=0; /* Next header type to process */ + int expected=0; /* Next protocol expected */ + bool finished=false; /* Loop breaking flag */ + bool unknown_hdr=false; /* Indicates unknown header found */ + IPv4Header ip4; + IPv6Header ip6; + TCPHeader tcp; + UDPHeader udp; + ICMPv4Header icmp4; + ICMPv6Header icmp6; + EthernetHeader eth; + DestOptsHeader ext_dopts; + FragmentHeader ext_frag; + HopByHopHeader ext_hopt; + RoutingHeader ext_routing; + ARPHeader arp; + memset(this_packet, 0, sizeof(this_packet)); + + /* Decide which layer we have to start from */ + if( eth_included ){ + next_layer=LINK_LAYER; + expected=HEADER_TYPE_ETHERNET; + }else{ + next_layer=NETWORK_LAYER; + } + + /* Header processing loop */ + while(!finished && curr_pktlen>0 && current_header<MAX_HEADERS_IN_PACKET){ + /* Ethernet and ARP headers ***********************************************/ + if(next_layer==LINK_LAYER ){ + if(PKTPARSERDEBUG)puts("Next Layer=Link"); + if(expected==HEADER_TYPE_ETHERNET){ + if(PKTPARSERDEBUG)puts("Expected Layer=Ethernet"); + if(eth.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (ethlen=eth.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + /* Determine next header type */ + switch( eth.getEtherType() ){ + case ETHTYPE_IPV4: + expected=HEADER_TYPE_IPv4; + next_layer=NETWORK_LAYER; + break; + case ETHTYPE_IPV6: + expected=HEADER_TYPE_IPv6; + next_layer=NETWORK_LAYER; + break; + case ETHTYPE_ARP: + next_layer=LINK_LAYER; + expected=HEADER_TYPE_ARP; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=ethlen; + this_packet[current_header++].type=HEADER_TYPE_ETHERNET; + eth.reset(); + curr_pkt+=ethlen; + curr_pktlen-=ethlen; + }else if(expected==HEADER_TYPE_ARP){ + if(PKTPARSERDEBUG)puts("Expected Layer=ARP"); + if(arp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (arplen=arp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + this_packet[current_header].length=arplen; + this_packet[current_header++].type=HEADER_TYPE_ARP; + arp.reset(); + curr_pkt+=arplen; + curr_pktlen-=arplen; + if(curr_pktlen>0){ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + }else{ + finished=true; + } + }else{ + assert(finished==true); + } + /* IPv4 and IPv6 headers **************************************************/ + }else if(next_layer==NETWORK_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=Network"); + /* Determine IP version */ + if (ip4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + + /* IP version 4 ---------------------------------*/ + if(ip4.getVersion()==4){ + if( (iplen=ip4.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + /* Determine next header type */ + switch(ip4.getNextProto()){ + case HEADER_TYPE_ICMPv4: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv4; + break; + case HEADER_TYPE_IPv4: /* IP in IP */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv4 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=iplen; + this_packet[current_header++].type=HEADER_TYPE_IPv4; + ip4.reset(); + curr_pkt+=iplen; + curr_pktlen-=iplen; + /* IP version 6 ---------------------------------*/ + }else if(ip4.getVersion()==6){ + ip4.reset(); + if (ip6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (ip6len=ip6.validate())==OP_FAILURE ){ + unknown_hdr=true; + break; + } + switch( ip6.getNextHeader() ){ + case HEADER_TYPE_ICMPv6: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv6; + break; + case HEADER_TYPE_IPv4: /* IPv4 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + case HEADER_TYPE_IPv6_HOPOPT: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_HOPOPT; + break; + case HEADER_TYPE_IPv6_OPTS: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_OPTS; + break; + case HEADER_TYPE_IPv6_ROUTE: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_ROUTE; + break; + case HEADER_TYPE_IPv6_FRAG: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_FRAG; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + this_packet[current_header].length=ip6len; + this_packet[current_header++].type=HEADER_TYPE_IPv6; + ip6.reset(); + curr_pkt+=ip6len; + curr_pktlen-=ip6len; + /* Bogus IP version -----------------------------*/ + }else{ + /* Wrong IP version, treat as raw data. */ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + } + /* TCP, UDP, ICMPv4 and ICMPv6 headers ************************************/ + }else if(next_layer==TRANSPORT_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=Transport"); + if(expected==HEADER_TYPE_TCP){ + if(PKTPARSERDEBUG)puts("Expected Layer=TCP"); + if(tcp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (tcplen=tcp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + expected=HEADER_TYPE_RAW_DATA; + this_packet[current_header].length=tcplen; + this_packet[current_header++].type=HEADER_TYPE_TCP; + tcp.reset(); + curr_pkt+=tcplen; + curr_pktlen-=tcplen; + next_layer=APPLICATION_LAYER; + }else if(expected==HEADER_TYPE_UDP){ + if(PKTPARSERDEBUG)puts("Expected Layer=UDP"); + if(udp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (udplen=udp.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + expected=HEADER_TYPE_RAW_DATA; + this_packet[current_header].length=udplen; + this_packet[current_header++].type=HEADER_TYPE_UDP; + udp.reset(); + curr_pkt+=udplen; + curr_pktlen-=udplen; + next_layer=APPLICATION_LAYER; + }else if(expected==HEADER_TYPE_ICMPv4){ + if(PKTPARSERDEBUG)puts("Expected Layer=ICMPv4"); + if(icmp4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (icmplen=icmp4.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + switch( icmp4.getType() ){ + /* Types that include an IPv4 packet as payload */ + case ICMP_UNREACH: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + /* ICMP types that include misc payloads (or no payload) */ + default: + expected=HEADER_TYPE_RAW_DATA; + next_layer=APPLICATION_LAYER; + break; + } + this_packet[current_header].length=icmplen; + this_packet[current_header++].type=HEADER_TYPE_ICMPv4; + icmp4.reset(); + curr_pkt+=icmplen; + curr_pktlen-=icmplen; + }else if(expected==HEADER_TYPE_ICMPv6){ + if(PKTPARSERDEBUG)puts("Expected Layer=ICMPv6"); + if(icmp6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ + unknown_hdr=true; + break; + } + if( (icmplen=icmp6.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + switch( icmp6.getType() ){ + /* Types that include an IPv6 packet as payload */ + case ICMPv6_UNREACH: + case ICMPv6_PKTTOOBIG: + case ICMPv6_TIMXCEED: + case ICMPv6_PARAMPROB: + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + /* ICMPv6 types that include misc payloads (or no payload) */ + default: + expected=HEADER_TYPE_RAW_DATA; + next_layer=APPLICATION_LAYER; + break; + } + this_packet[current_header].length=icmplen; + this_packet[current_header++].type=HEADER_TYPE_ICMPv6; + icmp6.reset(); + curr_pkt+=icmplen; + curr_pktlen-=icmplen; + }else{ + /* Wrong application layer protocol, treat as raw data. */ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + } + + /* IPv6 Extension Headers */ + }else if(next_layer==EXTHEADERS_LAYER){ + if(PKTPARSERDEBUG)puts("Next Layer=ExtHdr"); + u8 ext_next=0; + /* Hop-by-Hop Options */ + if(expected==HEADER_TYPE_IPv6_HOPOPT){ + if(PKTPARSERDEBUG)puts("Expected=Hopt"); + if(ext_hopt.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_hopt.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_hopt.getNextHeader(); + ext_hopt.reset(); + /* Routing Header */ + }else if(expected==HEADER_TYPE_IPv6_ROUTE){ + if(PKTPARSERDEBUG)puts("Expected=Route"); + if(ext_routing.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_routing.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_routing.getNextHeader(); + ext_routing.reset(); + /* Fragmentation Header */ + }else if(expected==HEADER_TYPE_IPv6_FRAG){ + if(PKTPARSERDEBUG)puts("Expected=Frag"); + if(ext_frag.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_frag.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_frag.getNextHeader(); + ext_frag.reset(); + /* Destination Options Header */ + }else if(expected==HEADER_TYPE_IPv6_OPTS){ + if(PKTPARSERDEBUG)puts("Expected=Dopts"); + if(ext_dopts.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ + unknown_hdr=true; + break; + } + if( (exthdrlen=ext_dopts.validate())==OP_FAILURE){ + unknown_hdr=true; + break; + } + ext_next=ext_dopts.getNextHeader(); + ext_dopts.reset(); + }else{ + /* Should never happen. */ + unknown_hdr=true; + break; + } + + /* Update the info for this header */ + this_packet[current_header].length=exthdrlen; + this_packet[current_header++].type=expected; + curr_pkt+=exthdrlen; + curr_pktlen-=exthdrlen; + + /* Lets's see what comes next */ + switch(ext_next){ + case HEADER_TYPE_ICMPv6: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_ICMPv6; + break; + case HEADER_TYPE_IPv4: /* IPv4 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv4; + break; + case HEADER_TYPE_TCP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_TCP; + break; + case HEADER_TYPE_UDP: + next_layer=TRANSPORT_LAYER; + expected=HEADER_TYPE_UDP; + break; + case HEADER_TYPE_IPv6: /* IPv6 in IPv6 */ + next_layer=NETWORK_LAYER; + expected=HEADER_TYPE_IPv6; + break; + case HEADER_TYPE_IPv6_HOPOPT: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_HOPOPT; + break; + case HEADER_TYPE_IPv6_OPTS: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_OPTS; + break; + case HEADER_TYPE_IPv6_ROUTE: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_ROUTE; + break; + case HEADER_TYPE_IPv6_FRAG: + next_layer=EXTHEADERS_LAYER; + expected=HEADER_TYPE_IPv6_FRAG; + break; + default: + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + break; + } + + /* Miscellaneous payloads *************************************************/ + }else{ // next_layer==APPLICATION_LAYER + if(PKTPARSERDEBUG)puts("Next Layer=Application"); + + /* If we get here it is possible that the packet is ARP but + * we have no access to the original Ethernet header. We + * determine if this header is ARP by checking its size + * and checking for some common values. */ + if(arp.storeRecvData(curr_pkt, curr_pktlen)!=OP_FAILURE){ + if( (arplen=arp.validate())!=OP_FAILURE){ + if(arp.getHardwareType()==HDR_ETH10MB){ + if(arp.getProtocolType()==0x0800){ + if(arp.getHwAddrLen()==ETH_ADDRESS_LEN){ + if(arp.getProtoAddrLen()==IPv4_ADDRESS_LEN){ + this_packet[current_header].length=arplen; + this_packet[current_header++].type=HEADER_TYPE_ARP; + arp.reset(); + curr_pkt+=arplen; + curr_pktlen-=arplen; + if(curr_pktlen>0){ + next_layer=APPLICATION_LAYER; + expected=HEADER_TYPE_RAW_DATA; + }else{ + finished=true; + } + } + } + } + } + } + } + + //if(expected==HEADER_TYPE_DNS){ + //}else if(expected==HEADER_TYPE_HTTP){ + //}... ETC + this_packet[current_header].length=curr_pktlen; + this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; + curr_pktlen=0; + finished=true; + } + } /* End of header processing loop */ + + /* If we couldn't validate some header, treat that header and any remaining + * data, as raw application data. */ + if (unknown_hdr==true){ + if(curr_pktlen>0){ + if(PKTPARSERDEBUG)puts("Unknown layer found. Treating it as raw data."); + this_packet[current_header].length=curr_pktlen; + this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; + } + } + + return this_packet; +} /* End of parse_received_packet() */ + + +/* TODO: remove */ +int PacketParser::dummy_print_packet_type(const u8 *pkt, size_t pktlen, bool eth_included){ + pkt_type_t *packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included); + for(int i=0; packetheaders[i].length!=0; i++){ + printf("%s:", header_type2string(packetheaders[i].type)); + } + printf("\n"); + return OP_SUCCESS; +} /* End of dummy_print_packet_type() */ + + +int PacketParser::dummy_print_packet(const u8 *pkt, size_t pktlen, bool eth_included){ + PacketElement *me=NULL, *aux=NULL; + if( (me=split(pkt, pktlen, eth_included))==NULL ) + return OP_FAILURE; + else{ + me->print(stdout, PRINT_DETAIL_HIGH); + printf("\n"); + } + /* Free the structs */ + while(me!=NULL){ + aux=me->getNextElement(); + delete me; + me=aux; + } + return OP_SUCCESS; +} /* End of dummy_print_packet() */ + + + +/** For a given packet, this method determines where the application layer data + * begins. It returs a positive offset if any application data was found, zero + * if the packet did not contain application data and a negative integer in + * case of error. */ +int PacketParser::payload_offset(const u8 *pkt, size_t pktlen, bool link_included){ + PacketElement *me=NULL, *aux=NULL; + size_t offset=pktlen; /* Initially, point to the end of the packet. */ + + /* Safe checks*/ + if(pkt==NULL || pktlen<=0) + return -1; + + dummy_print_packet_type(pkt, pktlen, link_included); + + /* Split the packet into separate protocol headers */ + if( (me=split(pkt, pktlen, link_included))==NULL ) + return -2; + else{ + aux=me; + } + + /* Find if there is application data and where it begins */ + while(me!=NULL){ + /* When we find application data, we compute the offset by substacting the + length of the application data from the packet's total length */ + if(me->protocol_id()==HEADER_TYPE_RAW_DATA){ + offset = pktlen - me->getLen(); + break; + } + me = me->getNextElement(); + } + + /* Free the structs */ + me=aux; + while(me!=NULL){ + aux=me->getNextElement(); + delete me; + me=aux; + } + + /* Return 0 if we didn't find any application data */ + if(offset==pktlen){ + return 0; + }else{ + return offset; + } +} /* End of payload_offset() */ + + + + +PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen){ + return split(pkt, pktlen, false); +} /* End of split() */ + + +PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen, bool eth_included){ + pkt_type_t *packetheaders=NULL; + const u8 *curr_pkt=pkt; + PacketElement *first=NULL; + PacketElement *last=NULL; + IPv4Header *ip4=NULL; + IPv6Header *ip6=NULL; + DestOptsHeader *ext_dopts=NULL; + FragmentHeader *ext_frag=NULL; + HopByHopHeader *ext_hopt=NULL; + RoutingHeader *ext_routing=NULL; + TCPHeader *tcp=NULL; + UDPHeader *udp=NULL; + ICMPv4Header *icmp4=NULL; + ICMPv6Header *icmp6=NULL; + EthernetHeader *eth=NULL; + ARPHeader *arp=NULL; + RawData *raw=NULL; + + /* Analyze the packet. This returns a list of header types and lengths */ + if((packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included))==NULL) + return NULL; + + /* Store each header in its own PacketHeader object type */ + for(int i=0; packetheaders[i].length!=0; i++){ + + switch(packetheaders[i].type){ + + case HEADER_TYPE_ETHERNET: + eth=new EthernetHeader(); + eth->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=eth; + }else{ + last->setNextElement(eth); + } + last=eth; + break; + + case HEADER_TYPE_ARP: + arp=new ARPHeader(); + arp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=arp; + }else{ + last->setNextElement(arp); + } + last=arp; + break; + + case HEADER_TYPE_IPv4: + ip4=new IPv4Header(); + ip4->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ip4; + }else{ + last->setNextElement(ip4); + } + last=ip4; + break; + + case HEADER_TYPE_IPv6: + ip6=new IPv6Header(); + ip6->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ip6; + }else{ + last->setNextElement(ip6); + } + last=ip6; + break; + + case HEADER_TYPE_TCP: + tcp=new TCPHeader(); + tcp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=tcp; + }else{ + last->setNextElement(tcp); + } + last=tcp; + break; + + case HEADER_TYPE_UDP: + udp=new UDPHeader(); + udp->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=udp; + }else{ + last->setNextElement(udp); + } + last=udp; + break; + + case HEADER_TYPE_ICMPv4: + icmp4=new ICMPv4Header(); + icmp4->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=icmp4; + }else{ + last->setNextElement(icmp4); + } + last=icmp4; + break; + + case HEADER_TYPE_ICMPv6: + icmp6=new ICMPv6Header(); + icmp6->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=icmp6; + }else{ + last->setNextElement(icmp6); + } + last=icmp6; + break; + + case HEADER_TYPE_IPv6_HOPOPT: + ext_hopt=new HopByHopHeader(); + ext_hopt->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_hopt; + }else{ + last->setNextElement(ext_hopt); + } + last=ext_hopt; + break; + + case HEADER_TYPE_IPv6_ROUTE: + ext_routing=new RoutingHeader(); + ext_routing->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_routing; + }else{ + last->setNextElement(ext_routing); + } + last=ext_routing; + break; + + case HEADER_TYPE_IPv6_FRAG: + ext_frag=new FragmentHeader(); + ext_frag->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_frag; + }else{ + last->setNextElement(ext_frag); + } + last=ext_frag; + break; + + case HEADER_TYPE_IPv6_OPTS: + ext_dopts=new DestOptsHeader(); + ext_dopts->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=ext_dopts; + }else{ + last->setNextElement(ext_dopts); + } + last=ext_dopts; + break; + + case HEADER_TYPE_RAW_DATA: + default: + raw=new RawData(); + raw->storeRecvData(curr_pkt, packetheaders[i].length); + if(first==NULL){ + first=raw; + }else{ + last->setNextElement(raw); + } + last=raw; + break; + } + curr_pkt+=packetheaders[i].length; + } + return first; +} /* End of split() */ + + +/* This method frees a chain of PacketElement objects. Note that objects in + * the chain are freed by calling "delete" on them, so only those instances + * that have been obtained through a call to "new" should be passed to this + * method. Chains returned by PacketParser::split() are safe to use with this.*/ +int PacketParser::freePacketChain(PacketElement *first){ + PacketElement *curr=first; + PacketElement *next=NULL; + while(curr!=NULL){ + next=curr->getNextElement(); + delete curr; + curr=next; + } + return OP_SUCCESS; +} /* End of freePacketChain() */ + + +/* This method is for debugging purposes only. It tests the packet parser and + * the PacketElement class family. Basically it checks that the supplied + * chain of PacketElements can be serialized and de-serialized correctly. + * Returns NULL on success or an error string in case of failure. */ +const char *PacketParser::test_packet_parser(PacketElement *test_pkt){ + const char *errmsg=NULL; + PacketElement *parsed_pkt=NULL; + PacketElement *orig_pkt=NULL; + PacketElement *new_pkt=NULL; + u8 *mypktbuff2=NULL; + u8 *mypktbuff=NULL; + + if(test_pkt==NULL){ + errmsg="NULL pointer supplied"; + goto end; + } + + /* Generate a serialized version of the packet */ + mypktbuff=(u8 *)safe_malloc(test_pkt->getLen()); + test_pkt->dumpToBinaryBuffer(mypktbuff, test_pkt->getLen()); + + /* Generate a chain of PacketElement objects from the serialized version. */ + parsed_pkt=PacketParser::split(mypktbuff, test_pkt->getLen()); + + if(parsed_pkt==NULL){ + errmsg="PacketParser::split() returned NULL"; + goto end; + } + if(parsed_pkt->getLen()!=test_pkt->getLen()){ + errmsg="Packets have different lengths"; + goto end; + } + + /* Generate a serialized version of the new chain */ + mypktbuff2=(u8 *)safe_malloc(parsed_pkt->getLen()); + parsed_pkt->dumpToBinaryBuffer(mypktbuff2, parsed_pkt->getLen()); + + /* Make sure both packets produce the exact same binary buffer */ + if(memcmp(mypktbuff, mypktbuff2, parsed_pkt->getLen())!=0){ + errmsg="The two packets do not result in the same binary buffer"; + goto end; + } + + /* Now let's check that both chains have the same number and type of + * PacketElements. */ + orig_pkt=test_pkt; + new_pkt=parsed_pkt; + while(orig_pkt!=NULL && new_pkt!=NULL){ + if(orig_pkt->protocol_id() != new_pkt->protocol_id() ){ + errmsg="Protocol IDs do not match"; + goto end; + } + orig_pkt=orig_pkt->getNextElement(); + new_pkt=new_pkt->getNextElement(); + } + + if(orig_pkt!=NULL || new_pkt!=NULL){ + errmsg="The two packets do not have the same number of chained elements."; + goto end; + } + + end: + /* Free our allocations */ + if(mypktbuff!=NULL) + free(mypktbuff); + if(mypktbuff2!=NULL) + free(mypktbuff2); + if(parsed_pkt!=NULL) + PacketParser::freePacketChain(parsed_pkt); + + /* If everything went well, errmsg should still be NULL. Otherwise it + * should point to an error message.*/ + return errmsg; +} + + + +/* Returns true if the supplied "rcvd" packet is a response to the "sent" packet. + * This method currently handles IPv4, IPv6, ICMPv4, ICMPv6, TCP and UDP. Here + * some examples of what can be matched using it: + * + * Probe: TCP SYN -> Response TCP SYN|ACK + * Probe: TCP SYN -> Response TCP RST|ACK + * Probe: UDP:53 -> Response UDP from port 53. + * Probe ICMP Echo -> Response ICMP Echo reply + * Probe ICMPv6 Neighbor Solicitation -> Response ICMPv6 Neighbor Advert + * Probe Malformed IPv6 -> Response ICMPv6 Parameter Problem + * Probe MLDv1 Query -> Response MLDv1 Report + * Probe ICMP Timestamp request -> Response ICMP timestamp response + * etc... + * + * Note that ICMP error messages are matched against sent probes (e.g: an ICMP + * Parameter Problem generated as a result of an invalid TCP segment is matched + * positively with the original TCP segment). Therefore, the caller must ensure + * that the received packet is what it expects before using it (e.g: the packet + * is an actual TCP packet, not an ICMP error). + * + * Warning: this method assumes that the probes you send are reasonably + * different from each other. Don't expect a 100% accuracy if you send a bunch + * of TCP segments with the same source and destination port numbers, or a + * bunch of ICMP messages with the same identifier and sequence number. */ +bool PacketParser::is_response(PacketElement *sent, PacketElement *rcvd){ + if(PKTPARSERDEBUG)printf("%s(): called\n", __func__); + + if(sent==NULL || rcvd==NULL) + return false; + + /* If any of the packets is encapsulated in an Ethernet frame, strip the + * link layer header before proceeding with the matching process. */ + if(rcvd->protocol_id()==HEADER_TYPE_ETHERNET) + if( (rcvd=rcvd->getNextElement())==NULL) + return false; + if(sent->protocol_id()==HEADER_TYPE_ETHERNET) + if( (sent=sent->getNextElement())==NULL) + return false; + + /* Make sure both packets have the same network layer */ + if(rcvd->protocol_id()!=sent->protocol_id()) + return false; + + /* The packet could be ARP */ + if(rcvd->protocol_id()==HEADER_TYPE_ARP){ + ARPHeader *sent_arp=(ARPHeader *)sent; + ARPHeader *rcvd_arp=(ARPHeader *)rcvd; + switch(sent_arp->getOpCode()){ + case OP_ARP_REQUEST: + if(rcvd_arp->getOpCode()==OP_ARP_REPLY){ + /* TODO @todo: getTargetIP() and getSenderIP() should + * either return struct in_addr or IPAddress but not u32. */ + if(sent_arp->getTargetIP()==rcvd_arp->getSenderIP()) + if(sent_arp->getSenderIP()==rcvd_arp->getTargetIP()) + return true; + } + return false; + break; + + /* We only support ARP, not RARP or other weird stuff. Also, if + * we didn't send a request, then we don't expect any response */ + case OP_RARP_REQUEST: + case OP_DRARP_REQUEST: + case OP_INARP_REQUEST: + default: + return false; + break; + + } + return false; + } + + /* The packet is IPv4 or IPv6 */ + if(rcvd->protocol_id()!=HEADER_TYPE_IPv6 && rcvd->protocol_id()!=HEADER_TYPE_IPv4) + return false; + if(PKTPARSERDEBUG)printf("%s(): Both packets use IP.\n", __func__); + + /* Handle the network layer with a more specific class */ + NetworkLayerElement *rcvd_ip=(NetworkLayerElement *)rcvd; + NetworkLayerElement *sent_ip=(NetworkLayerElement *)sent; + + /* Ensure the packet comes from the host we sent the probe to */ + if( memcmp(rcvd_ip->getSourceAddress(), sent_ip->getDestinationAddress(), rcvd_ip->getAddressLength())!=0 ) + return false; + /* Ensure the received packet is destined to us */ + if( memcmp(rcvd_ip->getDestinationAddress(), sent_ip->getSourceAddress(), rcvd_ip->getAddressLength())!=0 ) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Src and Dst addresses make sense.\n", __func__); + + /* Skip layers until we find ICMP or a transport protocol */ + PacketElement *rcvd_layer4=rcvd_ip->getNextElement(); + PacketElement *sent_layer4=sent_ip->getNextElement(); + while(rcvd_layer4!=NULL){ + if(rcvd_layer4->protocol_id()==HEADER_TYPE_UDP || rcvd_layer4->protocol_id()==HEADER_TYPE_TCP || + rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4 || rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + rcvd_layer4=rcvd_layer4->getNextElement(); + } + } + while(sent_layer4!=NULL){ + if(sent_layer4->protocol_id()==HEADER_TYPE_UDP || sent_layer4->protocol_id()==HEADER_TYPE_TCP || + sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4 || sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + sent_layer4=sent_layer4->getNextElement(); + } + } + if(rcvd_layer4==NULL || sent_layer4==NULL) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Layer 4 found for both packets.\n", __func__); + + /* If we get here it means that both packets have a proper layer4 protocol + * header. Now we have to check which type are they and see if a probe-response + * relation can be established. */ + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 || sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + + if(PKTPARSERDEBUG)printf("%s(): Sent packet is ICMP.\n", __func__); + + /* Make sure received packet is ICMP (we only expect ICMP responses for + * ICMP probes) */ + if(rcvd_layer4->protocol_id()!=HEADER_TYPE_ICMPv6 && rcvd_layer4->protocol_id()!=HEADER_TYPE_ICMPv4 ) + return false; + + /* Make sure both packets have the same ICMP version */ + if(sent_layer4->protocol_id()!=rcvd_layer4->protocol_id()) + return false; + + if(PKTPARSERDEBUG)printf("%s(): Received packet is ICMP too.\n", __func__); + + /* Check if the received ICMP is an error message. We don't care which kind + * of error message it is. The only important thing is that error messages + * contain a copy of the original datagram, and that's what we want to + * match against the sent probe. */ + if( ((ICMPHeader *)rcvd_layer4)->isError() ){ + NetworkLayerElement *iperror=(NetworkLayerElement *)rcvd_layer4->getNextElement(); + + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an error message.\n", __func__); + + /* ICMP error message must contain the original datagram */ + if(iperror==NULL) + return false; + + /* The first header must be IP */ + if(iperror->protocol_id()!=HEADER_TYPE_IPv6 && iperror->protocol_id()!=HEADER_TYPE_IPv4) + return false; + + /* The IP version must match the probe's */ + if(iperror->protocol_id()!=sent_ip->protocol_id()) + return false; + + /* Source and destination addresses must match the probe's */ + if( memcmp(iperror->getSourceAddress(), sent_ip->getSourceAddress(), iperror->getAddressLength())!=0 ) + return false; + if( memcmp(iperror->getDestinationAddress(), sent_ip->getDestinationAddress(), iperror->getAddressLength())!=0 ) + return false; + + /* So far we've verified that the ICMP error contains an IP datagram that matches + * what we sent. Now, let's find the upper layer ICMP header (skip extension + * headers until we find ICMP) */ + ICMPHeader *inner_icmp=(ICMPHeader *)iperror->getNextElement(); + while(inner_icmp!=NULL){ + if(inner_icmp->protocol_id()==HEADER_TYPE_ICMPv4 || inner_icmp->protocol_id()==HEADER_TYPE_ICMPv6 ){ + break; + }else{ + inner_icmp=(ICMPHeader *)inner_icmp->getNextElement(); + } + } + if(inner_icmp==NULL) + return false; + + /* If we get here it means that we've found an ICMP header inside the + * ICMP error message that we received. First of all, check that the + * ICMP version matches what we sent. */ + if(sent_layer4->protocol_id() != inner_icmp->protocol_id()) + return false; + + /* Make sure ICMP type and code match */ + if( ((ICMPHeader*)sent_layer4)->getType() != inner_icmp->getType() ) + return false; + if( ((ICMPHeader*)sent_layer4)->getCode() != inner_icmp->getCode() ) + return false; + + /* Now go into a bit of detail and try to determine if both headers + * are equal, comparing the values of specific fields. */ + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6){ + ICMPv6Header *sent_icmp6=(ICMPv6Header *)sent_layer4; + ICMPv6Header *inner_icmp6=(ICMPv6Header *)inner_icmp; + + switch(sent_icmp6->getType()){ + case ICMPv6_UNREACH: + case ICMPv6_TIMXCEED : + /* For these we cannot guarantee that the received ICMPv6 error + * packet included data beyond the inner ICMPv6 header, so we just + * assume that they are a match to the sent probe. (We shouldn't + * really be sending ICMPv6 error messages and expect ICMPv6 error + * responses that contain our ICMv6P error messages, should we? + * Well, even if we do, there is a good chance we are able to match + * those responses with the original probe) */ + break; + + case ICMPv6_PKTTOOBIG: + if(sent_icmp6->getMTU() != inner_icmp6->getMTU()) + return false; + break; + + case ICMPv6_PARAMPROB: + if(sent_icmp6->getPointer() != inner_icmp6->getPointer()) + return false; + break; + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + if(sent_icmp6->getIdentifier() != inner_icmp6->getIdentifier()) + return false; + if(sent_icmp6->getSequence() != inner_icmp6->getSequence()) + return false; + break; + + case ICMPv6_ROUTERSOLICIT: + /* Here we do not have much to compare, so we just test that + * the reserved field contains the same value, usually zero. */ + if(sent_icmp6->getReserved()!=inner_icmp6->getReserved()) + return false; + break; + + case ICMPv6_ROUTERADVERT: + if(sent_icmp6->getCurrentHopLimit() != inner_icmp6->getCurrentHopLimit() ) + return false; + if(sent_icmp6->getRouterLifetime() != inner_icmp6->getRouterLifetime() ) + return false; + if(sent_icmp6->getReachableTime() != inner_icmp6->getReachableTime() ) + return false; + if(sent_icmp6->getRetransmissionTimer() != inner_icmp6->getRetransmissionTimer() ) + return false; + break; + + case ICMPv6_REDIRECT: + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, inner_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + if( memcmp(sent_icmp6->getDestinationAddress().s6_addr, inner_icmp6->getDestinationAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_NGHBRADVERT: + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, inner_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_RTRRENUM: + if(sent_icmp6->getSequence() != inner_icmp6->getSequence() ) + return false; + if(sent_icmp6->getSegmentNumber() != inner_icmp6->getSegmentNumber() ) + return false; + if(sent_icmp6->getMaxDelay() != inner_icmp6->getMaxDelay() ) + return false; + if(sent_icmp6->getFlags() != inner_icmp6->getFlags() ) + return false; + break; + + case ICMPv6_NODEINFOQUERY: + case ICMPv6_NODEINFORESP: + if(sent_icmp6->getNodeInfoFlags() != inner_icmp6->getNodeInfoFlags() ) + return false; + if(sent_icmp6->getNonce() != inner_icmp6->getNonce()) + return false; + if(sent_icmp6->getQtype() != inner_icmp6->getQtype() ) + return false; + break; + + + case ICMPv6_GRPMEMBQUERY: + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + case ICMPv6_INVNGHBRSOLICIT: + case ICMPv6_INVNGHBRADVERT: + case ICMPv6_MLDV2: + case ICMPv6_AGENTDISCOVREQ: + case ICMPv6_AGENTDISCOVREPLY: + case ICMPv6_MOBPREFIXSOLICIT: + case ICMPv6_MOBPREFIXADVERT: + case ICMPv6_CERTPATHSOLICIT: + case ICMPv6_CERTPATHADVERT: + case ICMPv6_EXPMOBILITY: + case ICMPv6_MRDADVERT: + case ICMPv6_MRDSOLICIT: + case ICMPv6_MRDTERMINATE: + case ICMPv6_FMIPV6: + /* All these types are not currently implemented but since the + * sent_icmp.getType() has returned such type, we assume + * that there is a match (don't return false here). */ + break; + + default: + /* Do not match ICMPv6 types we don't know about */ + return false; + break; + } + }else if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + ICMPv4Header *sent_icmp4=(ICMPv4Header *)sent_layer4; + ICMPv4Header *inner_icmp4=(ICMPv4Header *)inner_icmp; + + switch(sent_icmp4->getType()){ + case ICMP_ECHOREPLY: + case ICMP_ECHO: + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + case ICMP_INFO: + case ICMP_INFOREPLY: + case ICMP_MASK: + case ICMP_MASKREPLY: + case ICMP_DOMAINNAME: + case ICMP_DOMAINNAMEREPLY: + /* Check the message identifier and sequence number */ + if(sent_icmp4->getIdentifier() != inner_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != inner_icmp4->getSequence()) + return false; + break; + + case ICMP_ROUTERADVERT: + /* Check only the main fields, no need to parse the whole list + * of addresses (maybe we didn't even get enough octets to + * check that). */ + if(sent_icmp4->getNumAddresses() != inner_icmp4->getNumAddresses() ) + return false; + if(sent_icmp4->getAddrEntrySize() != inner_icmp4->getAddrEntrySize()) + return false; + if(sent_icmp4->getLifetime() != inner_icmp4->getLifetime() ) + return false; + break; + + case ICMP_ROUTERSOLICIT: + /* Here we do not have much to compare, so we just test that + * the reserved field contains the same value, usually zero. */ + if(sent_icmp4->getReserved()!=inner_icmp4->getReserved()) + return false; + break; + + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_TIMXCEED: + /* For these we cannot guarantee that the received ICMP error + * packet included data beyond the inner ICMP header, so we just + * assume that they are a match to the sent probe. (We shouldn't + * really be sending ICMP error messages and expect ICMP error + * responses that contain our ICMP error messages, should we? + * Well, even if we do, there is a good chance we are able to match + * those responses with the original probe) */ + break; + + case ICMP_REDIRECT: + if(sent_icmp4->getGatewayAddress().s_addr != inner_icmp4->getGatewayAddress().s_addr) + return false; + break; + + case ICMP_PARAMPROB: + if(sent_icmp4->getParameterPointer() != inner_icmp4->getParameterPointer()) + return false; + break; + + case ICMP_TRACEROUTE: + if(sent_icmp4->getIDNumber() != inner_icmp4->getIDNumber()) + return false; + if(sent_icmp4->getOutboundHopCount() != inner_icmp4->getOutboundHopCount()) + return false; + if(sent_icmp4->getOutputLinkSpeed() != inner_icmp4->getOutputLinkSpeed() ) + return false; + if(sent_icmp4->getOutputLinkMTU() != inner_icmp4->getOutputLinkMTU() ) + return false; + break; + + case ICMP_SECURITYFAILURES: + /* Check the pointer and the reserved field */ + if(sent_icmp4->getSecurityPointer() != inner_icmp4->getSecurityPointer()) + return false; + if(sent_icmp4->getReserved() != inner_icmp4->getReserved()) + return false; + break; + + default: + /* Do not match ICMP types we don't know about */ + return false; + break; + } + }else{ + return false; // Should never happen, though. + } + }else{ /* Received ICMP is informational. */ + + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an informational message.\n", __func__); + + /* If we get here it means that we received an informational ICMPv6 + * message. So now we have to check if the received message is the + * expected reply to the probe we sent (like an Echo reply for an Echo + * request, etc). */ + + if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv6 && rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6){ + ICMPv6Header *sent_icmp6=(ICMPv6Header *)sent_layer4; + ICMPv6Header *rcvd_icmp6=(ICMPv6Header *)rcvd_layer4; + + switch( sent_icmp6->getType() ){ + + case ICMPv6_UNREACH: + case ICMPv6_TIMXCEED : + case ICMPv6_PKTTOOBIG: + case ICMPv6_PARAMPROB: + /* This should never happen. If we got here, the received type + * should be of an informational message, not an error message. */ + printf("Error in isResponse()\n"); + return false; + break; + + case ICMPv6_ECHO: + /* For Echo request, we expect echo replies */ + if(rcvd_icmp6->getType()!=ICMPv6_ECHOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp6->getIdentifier() != rcvd_icmp6->getIdentifier()) + return false; + if(sent_icmp6->getSequence() != rcvd_icmp6->getSequence()) + return false; + break; + + case ICMPv6_ECHOREPLY: + /* We don't expect replies to Echo replies */ + return false; + break; + + case ICMPv6_ROUTERSOLICIT: + /* For Router solicitations, we expect Router advertisements. + * We only check if the received ICMP is a router advert because + * there is nothing else that can be used to match the solicitation + * with the response. */ + if(rcvd_icmp6->getType()!=ICMPv6_ROUTERADVERT) + return false; + break; + + case ICMPv6_ROUTERADVERT: + /* We don't expect replies to router advertisements */ + return false; + break; + + case ICMPv6_REDIRECT: + /* We don't expect replies to Redirect messages */ + return false; + break; + + case ICMPv6_NGHBRSOLICIT: + if(PKTPARSERDEBUG)printf("%s(): Sent ICMP is an ICMPv6 Neighbor Solicitation.\n", __func__); + /* For Neighbor solicitations, we expect Neighbor advertisements + * with the "S" flag set (solicited flag) and the same address + * in the "TargetAddress" field. */ + if(rcvd_icmp6->getType()!=ICMPv6_NGHBRADVERT) + return false; + if(PKTPARSERDEBUG)printf("%s(): Received ICMP is an ICMPv6 Neighbor Advertisement.\n", __func__); + if( !(rcvd_icmp6->getFlags() & 0x40) ) + return false; + if( memcmp(sent_icmp6->getTargetAddress().s6_addr, rcvd_icmp6->getTargetAddress().s6_addr, 16) !=0 ) + return false; + break; + + case ICMPv6_NGHBRADVERT: + /* We don't expect replies to Neighbor advertisements */ + return false; + break; + + case ICMPv6_NODEINFOQUERY: + /* For Node Information Queries we expect Node Information + * responses with the same Nonce value that we used in the query. */ + if(rcvd_icmp6->getType()!=ICMPv6_NODEINFORESP) + return false; + if(sent_icmp6->getNonce() != rcvd_icmp6->getNonce()) + return false; + break; + + case ICMPv6_NODEINFORESP: + /* Obviously, we do not expect responses to a response */ + return false; + break; + + case ICMPv6_INVNGHBRSOLICIT: + /* For Inverse Neighbor Discovery Solicitations we expect + * advertisements in response. We don't do any additional + * validation since any advert can be considered a response + * to the solicitation. */ + if(rcvd_icmp6->getType()!=ICMPv6_INVNGHBRADVERT) + return false; + break; + + case ICMPv6_INVNGHBRADVERT: + /* We don't expect responses to advertisements */ + return false; + break; + + + case ICMPv6_RTRRENUM: + /* We don't expect specific responses to router renumbering + * messages. */ + return false; + break; + + case ICMPv6_GRPMEMBQUERY: + /* For Multicast Listener Discovery (MLD) queries, we expect + * either MLD Responses or MLD Done messages. We can't handle MLDv2 + * yet, so we don't match it. TODO: Implement support for MLDv2 */ + if(rcvd_icmp6->getType()!=ICMPv6_GRPMEMBREP && rcvd_icmp6->getType()!=ICMPv6_GRPMEMBRED) + return false; + /* Now we have two possibilities: + * a) The query is a "General Query" where the multicast address + * is set to zero. + * b) The query is a "Multicast-Address-Specific Query", where + * the multicast address field is set to an actual multicast + * address. + * In the first case, we match any query response to the request, + * as we don't have a multicast address to compare. In the second + * case, we verify that the target mcast address of the query + * matches the one in the response. */ + struct in6_addr zeroaddr; + memset(&zeroaddr, 0, sizeof(struct in6_addr)); + if( memcmp( sent_icmp6->getMulticastAddress().s6_addr, zeroaddr.s6_addr, 16) != 0 ){ /* Case B: */ + if (memcmp( sent_icmp6->getMulticastAddress().s6_addr, rcvd_icmp6->getMulticastAddress().s6_addr, 16)!=0 ) + return false; + } + break; + + case ICMPv6_GRPMEMBREP: + case ICMPv6_GRPMEMBRED: + /* We don't expect responses to MLD reports */ + return false; + break; + + case ICMPv6_MLDV2: + case ICMPv6_AGENTDISCOVREQ: + case ICMPv6_AGENTDISCOVREPLY: + case ICMPv6_MOBPREFIXSOLICIT: + case ICMPv6_MOBPREFIXADVERT: + case ICMPv6_CERTPATHSOLICIT: + case ICMPv6_CERTPATHADVERT: + case ICMPv6_EXPMOBILITY: + case ICMPv6_MRDADVERT: + case ICMPv6_MRDSOLICIT: + case ICMPv6_MRDTERMINATE: + case ICMPv6_FMIPV6: + default: + /* Do not match ICMPv6 types we don't implement or know about * + * TODO: Implement these ICMPv6 types. */ + return false; + break; + + } + + }else if(sent_layer4->protocol_id()==HEADER_TYPE_ICMPv4 && rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + ICMPv4Header *sent_icmp4=(ICMPv4Header *)sent_layer4; + ICMPv4Header *rcvd_icmp4=(ICMPv4Header *)rcvd_layer4; + + switch( sent_icmp4->getType() ){ + + case ICMP_ECHOREPLY: + /* We don't expect replies to Echo replies. */ + return false; + break; + + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + /* Nodes are not supposed to respond to error messages, so + * we don't expect any replies. */ + return false; + break; + + case ICMP_ECHO: + /* For Echo request, we expect echo replies */ + if(rcvd_icmp4->getType()!=ICMP_ECHOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_ROUTERSOLICIT: + /* For ICMPv4 router solicitations, we expect router advertisements. + * We don't validate anything else because in IPv4 any advert that + * comes from the host we sent the solicitation to can be + * considered a response. */ + if(rcvd_icmp4->getType()!=ICMP_ROUTERADVERT) + return false; + break; + + case ICMP_ROUTERADVERT: + /* We don't expect responses to advertisements */ + return false; + break; + + case ICMP_TSTAMP: + /* For Timestampt requests, we expect timestamp replies */ + if(rcvd_icmp4->getType()!=ICMP_TSTAMPREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_TSTAMPREPLY: + /* We do not expect responses to timestamp replies */ + return false; + break; + + case ICMP_INFO: + /* For Information requests, we expect Information replies */ + if(rcvd_icmp4->getType()!=ICMP_INFOREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_INFOREPLY: + /* We do not expect responses to Information replies */ + return false; + break; + + case ICMP_MASK: + /* For Netmask requests, we expect Netmask replies */ + if(rcvd_icmp4->getType()!=ICMP_MASKREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_MASKREPLY: + /* We do not expect responses to netmask replies */ + return false; + break; + + case ICMP_TRACEROUTE: + /* We don't expect replies to a traceroute message as it is + * sent as a response to an IP datagram that contains the + * IP traceroute option. Also, note that this function does + * not take this into account when processing IPv4 datagrams + * so if we receive an ICMP_TRACEROUTE we'll not be able + * to match it with the original IP datagram. */ + return false; + break; + + case ICMP_DOMAINNAME: + /* For Domain Name requests, we expect Domain Name replies */ + if(rcvd_icmp4->getType()!=ICMP_DOMAINNAMEREPLY) + return false; + /* And we expect the ID and sequence number of the reply to + * match the ID and seq of the request. */ + if(sent_icmp4->getIdentifier() != rcvd_icmp4->getIdentifier()) + return false; + if(sent_icmp4->getSequence() != rcvd_icmp4->getSequence()) + return false; + break; + + case ICMP_DOMAINNAMEREPLY: + /* We do not expect replies to DN replies */ + return false; + break; + + case ICMP_SECURITYFAILURES: + /* Nodes are not expected to send replies to this message, as it + * is an ICMP error. */ + return false; + break; + } + }else{ + return false; // Should never happen + } + } + }else if(sent_layer4->protocol_id()==HEADER_TYPE_TCP || sent_layer4->protocol_id()==HEADER_TYPE_UDP){ + + if(PKTPARSERDEBUG)printf("%s(): Sent packet has a transport layer header.\n", __func__); + + /* Both are TCP or both UDP */ + if(sent_layer4->protocol_id()==rcvd_layer4->protocol_id()){ + + if(PKTPARSERDEBUG)printf("%s(): Received packet has a transport layer header too.\n", __func__); + + /* Probe source port must equal response target port */ + if( ((TransportLayerElement *)sent_layer4)->getSourcePort() != ((TransportLayerElement *)rcvd_layer4)->getDestinationPort() ) + return false; + /* Probe target port must equal response source port */ + if( ((TransportLayerElement *)rcvd_layer4)->getSourcePort() != ((TransportLayerElement *)sent_layer4)->getDestinationPort() ) + return false; + + /* If we sent TCP or UDP and got ICMP in response, we need to find a copy of our packet in the + * ICMP payload, providing it is an ICMP error message. */ + }else if(rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv6 || rcvd_layer4->protocol_id()==HEADER_TYPE_ICMPv4){ + + if(PKTPARSERDEBUG)printf("%s(): Received packet does not have transport layer header but an ICMP header.\n", __func__); + + /* We only expect ICMP error messages */ + if( !(((ICMPHeader *)rcvd_layer4)->isError()) ) + return false; + + /* Let's validate the original header */ + NetworkLayerElement *iperror=(NetworkLayerElement *)rcvd_layer4->getNextElement(); + + /* ICMP error message must contain the original datagram */ + if(iperror==NULL) + return false; + + /* The first header must be IP */ + if(iperror->protocol_id()!=HEADER_TYPE_IPv6 && iperror->protocol_id()!=HEADER_TYPE_IPv4) + return false; + + /* The IP version must match the probe's */ + if(iperror->protocol_id()!=sent_ip->protocol_id()) + return false; + + /* Source and destination addresses must match the probe's (NATs are + * supposed to rewrite them too, so this should be OK) */ + if( memcmp(iperror->getSourceAddress(), sent_ip->getSourceAddress(), iperror->getAddressLength())!=0 ) + return false; + if( memcmp(iperror->getDestinationAddress(), sent_ip->getDestinationAddress(), iperror->getAddressLength())!=0 ) + return false; + + /* So far we've verified that the ICMP error contains an IP datagram that matches + * what we sent. Now, let's find the upper layer protocol (skip extension + * headers and the like until we find some transport protocol). */ + TransportLayerElement *layer4error=(TransportLayerElement *)iperror->getNextElement(); + while(layer4error!=NULL){ + if(layer4error->protocol_id()==HEADER_TYPE_UDP || layer4error->protocol_id()==HEADER_TYPE_TCP ){ + break; + }else{ + layer4error=(TransportLayerElement *)layer4error->getNextElement(); + } + } + if(layer4error==NULL) + return false; + + /* Now make sure we see the same port numbers */ + if( layer4error->getSourcePort() != ((TransportLayerElement *)sent_layer4)->getSourcePort() ) + return false; + if( layer4error->getDestinationPort() != ((TransportLayerElement *)sent_layer4)->getDestinationPort() ) + return false; + } else { + return false; + } + }else{ + /* We sent a layer 4 other than ICMP, ICMPv6, TCP, or UDP. We return false + * as we cannot match responses for protocols we don't understand */ + return false; + } + + /* If we get there it means the packet passed all the tests. Return true + * to indicate that the packet is a response to this FPProbe. */ + if(PKTPARSERDEBUG)printf("%s(): The received packet was successfully matched with the sent packet.\n", __func__); + return true; +} + +/* Tries to find a transport layer header in the supplied chain of + * protocol headers. On success it returns a pointer to a PacketElement + * of one of these types: + * + * HEADER_TYPE_TCP + * HEADER_TYPE_UDP + * HEADER_TYPE_ICMPv4 + * HEADER_TYPE_ICMPv6 + * HEADER_TYPE_SCTP + * HEADER_TYPE_ARP + * + * It returns NULL if no transport layer header is found. + * + * Note that this method only understands IPv4, IPv6 (and its + * extension headers) and Ethernet. If the supplied packet contains + * something different before the tranport layer, NULL will be returned. + * */ +PacketElement *PacketParser::find_transport_layer(PacketElement *chain){ + PacketElement *aux=chain; + /* Traverse the chain of PacketElements */ + while(aux!=NULL){ + switch(aux->protocol_id()){ + /* If we have a link or a network layer header, skip it. */ + case HEADER_TYPE_IPv6_HOPOPT: + case HEADER_TYPE_IPv4: + case HEADER_TYPE_IPv6: + case HEADER_TYPE_IPv6_ROUTE: + case HEADER_TYPE_IPv6_FRAG: + case HEADER_TYPE_IPv6_NONXT: + case HEADER_TYPE_IPv6_OPTS: + case HEADER_TYPE_ETHERNET: + case HEADER_TYPE_IPv6_MOBILE: + aux=aux->getNextElement(); + break; + + /* If we found the transport layer, return it. */ + case HEADER_TYPE_TCP: + case HEADER_TYPE_UDP: + case HEADER_TYPE_ICMPv4: + case HEADER_TYPE_ICMPv6: + case HEADER_TYPE_SCTP: + case HEADER_TYPE_ARP: + return aux; + break; + + /* Otherwise, the packet contains headers we don't understand + * so we just return NULL to indicate that no valid transport + * layer was found. */ + default: + return NULL; + break; + } + } + return NULL; +} /* End of find_transport_layer() */ |