/***************************************************************************
 * 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-2026 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;
                    }
                    continue;
                  }
                }
              }
            }
          }
        }

        //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 && current_header < MAX_HEADERS_IN_PACKET) {
    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;
    }
  }

  /* Ensure the sentinel value is correct: */
  assert(current_header <= MAX_HEADERS_IN_PACKET);
  this_packet[current_header].length = 0;
  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) */

      iperror = dynamic_cast<NetworkLayerElement *>(iperror->getNextElement());
      while(iperror!=NULL){
        if(iperror->protocol_id()==HEADER_TYPE_ICMPv4 || iperror->protocol_id()==HEADER_TYPE_ICMPv6 ){
            break;
        }else{
          iperror = dynamic_cast<NetworkLayerElement *>(iperror->getNextElement());
        }
      }
      if(iperror==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() != iperror->protocol_id())
        return false;

      /* Make sure ICMP type and code match  */
      ICMPHeader *inner_icmp = dynamic_cast<ICMPHeader *>(iperror);
      if (inner_icmp == NULL)
        return false;
      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). */
        iperror = dynamic_cast<NetworkLayerElement *>(iperror->getNextElement());
        while(iperror!=NULL){
          if(iperror->protocol_id()==HEADER_TYPE_UDP || iperror->protocol_id()==HEADER_TYPE_TCP ){
              break;
          }else{
            iperror = dynamic_cast<NetworkLayerElement *>(iperror->getNextElement());
          }
        }
        if(iperror==NULL)
          return false;

        /* Now make sure we see the same port numbers */
        TransportLayerElement *layer4error = dynamic_cast<TransportLayerElement *>(iperror);
        if (layer4error == NULL)
          return false;
        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() */
