Exemple #1
0
int DecodeGRE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
{
    uint16_t header_len = GRE_HDR_LEN;
    GRESreHdr *gsre = NULL;

    StatsIncr(tv, dtv->counter_gre);

    if(len < GRE_HDR_LEN)    {
        ENGINE_SET_INVALID_EVENT(p, GRE_PKT_TOO_SMALL);
        return TM_ECODE_FAILED;
    }

    p->greh = (GREHdr *)pkt;
    if(p->greh == NULL)
        return TM_ECODE_FAILED;

    SCLogDebug("p %p pkt %p GRE protocol %04x Len: %d GRE version %x",
        p, pkt, GRE_GET_PROTO(p->greh), len,GRE_GET_VERSION(p->greh));

    switch (GRE_GET_VERSION(p->greh))
    {
        case GRE_VERSION_0:

            /* GRE version 0 doenst support the fields below RFC 1701 */

            /**
             * \todo We need to make sure this does not allow bypassing
             *       inspection.  A server may just ignore these and
             *       continue processing the packet, but we will not look
             *       further into it.
             */

            if (GRE_FLAG_ISSET_RECUR(p->greh)) {
                ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_RECUR);
                return TM_ECODE_OK;
            }

            if (GREV1_FLAG_ISSET_FLAGS(p->greh))   {
                ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_FLAGS);
                return TM_ECODE_OK;
            }

            /* Adjust header length based on content */

            if (GRE_FLAG_ISSET_KY(p->greh))
                header_len += GRE_KEY_LEN;

            if (GRE_FLAG_ISSET_SQ(p->greh))
                header_len += GRE_SEQ_LEN;

            if (GRE_FLAG_ISSET_CHKSUM(p->greh) || GRE_FLAG_ISSET_ROUTE(p->greh))
                header_len += GRE_CHKSUM_LEN + GRE_OFFSET_LEN;

            if (header_len > len)   {
                ENGINE_SET_INVALID_EVENT(p, GRE_VERSION0_HDR_TOO_BIG);
                return TM_ECODE_OK;
            }

            if (GRE_FLAG_ISSET_ROUTE(p->greh))
            {
                while (1)
                {
                    if ((header_len + GRE_SRE_HDR_LEN) > len) {
                        ENGINE_SET_INVALID_EVENT(p,
                                                 GRE_VERSION0_MALFORMED_SRE_HDR);
                        return TM_ECODE_OK;
                    }

                    gsre = (GRESreHdr *)(pkt + header_len);

                    header_len += GRE_SRE_HDR_LEN;

                    if ((SCNtohs(gsre->af) == 0) && (gsre->sre_length == 0))
                        break;

                    header_len += gsre->sre_length;
                    if (header_len > len) {
                        ENGINE_SET_INVALID_EVENT(p,
                                                 GRE_VERSION0_MALFORMED_SRE_HDR);
                        return TM_ECODE_OK;
                    }
                }
            }
            break;

        case GRE_VERSION_1:

            /* GRE version 1 doenst support the fields below RFC 1701 */

            /**
             * \todo We need to make sure this does not allow bypassing
             *       inspection.  A server may just ignore these and
             *       continue processing the packet, but we will not look
             *       further into it.
             */

            if (GRE_FLAG_ISSET_CHKSUM(p->greh))    {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_CHKSUM);
                return TM_ECODE_OK;
            }

            if (GRE_FLAG_ISSET_ROUTE(p->greh)) {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_ROUTE);
                return TM_ECODE_OK;
            }

            if (GRE_FLAG_ISSET_SSR(p->greh))   {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_SSR);
                return TM_ECODE_OK;
            }

            if (GRE_FLAG_ISSET_RECUR(p->greh)) {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_RECUR);
                return TM_ECODE_OK;
            }

            if (GREV1_FLAG_ISSET_FLAGS(p->greh))   {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_FLAGS);
                return TM_ECODE_OK;
            }

            if (GRE_GET_PROTO(p->greh) != GRE_PROTO_PPP)  {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_WRONG_PROTOCOL);
                return TM_ECODE_OK;
            }

            if (!(GRE_FLAG_ISSET_KY(p->greh))) {
                ENGINE_SET_INVALID_EVENT(p,GRE_VERSION1_NO_KEY);
                return TM_ECODE_OK;
            }

            header_len += GRE_KEY_LEN;

            /* Adjust header length based on content */

            if (GRE_FLAG_ISSET_SQ(p->greh))
                header_len += GRE_SEQ_LEN;

            if (GREV1_FLAG_ISSET_ACK(p->greh))
                header_len += GREV1_ACK_LEN;

            if (header_len > len)   {
                ENGINE_SET_INVALID_EVENT(p, GRE_VERSION1_HDR_TOO_BIG);
                return TM_ECODE_OK;
            }

            break;
        default:
            ENGINE_SET_INVALID_EVENT(p, GRE_WRONG_VERSION);
            return TM_ECODE_OK;
    }

    switch (GRE_GET_PROTO(p->greh))
    {
        case ETHERNET_TYPE_IP:
            {
                if (pq != NULL) {
                    Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                            len - header_len, DECODE_TUNNEL_IPV4, pq);
                    if (tp != NULL) {
                        PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                        PacketEnqueue(pq,tp);
                    }
                }
                break;
            }

        case GRE_PROTO_PPP:
            {
                if (pq != NULL) {
                    Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                            len - header_len, DECODE_TUNNEL_PPP, pq);
                    if (tp != NULL) {
                        PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                        PacketEnqueue(pq,tp);
                    }
                }
                break;
            }

        case ETHERNET_TYPE_IPV6:
            {
                if (pq != NULL) {
                    Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                            len - header_len, DECODE_TUNNEL_IPV6, pq);
                    if (tp != NULL) {
                        PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                        PacketEnqueue(pq,tp);
                    }
                }
                break;
            }

        case ETHERNET_TYPE_VLAN:
            {
                if (pq != NULL) {
                    Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                            len - header_len, DECODE_TUNNEL_VLAN, pq);
                    if (tp != NULL) {
                        PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                        PacketEnqueue(pq,tp);
                    }
                }
                break;
            }

        case ETHERNET_TYPE_ERSPAN:
        {
            if (pq != NULL) {
                Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                        len - header_len, DECODE_TUNNEL_ERSPAN, pq);
                if (tp != NULL) {
                    PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                    PacketEnqueue(pq,tp);
                }
            }
            break;
        }

        case ETHERNET_TYPE_BRIDGE:
            {
                if (pq != NULL) {
                    Packet *tp = PacketTunnelPktSetup(tv, dtv, p, pkt + header_len,
                            len - header_len, DECODE_TUNNEL_ETHERNET, pq);
                    if (tp != NULL) {
                        PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE);
                        PacketEnqueue(pq,tp);
                    }
                }
                break;
            }

        default:
            return TM_ECODE_OK;
    }
    return TM_ECODE_OK;
}
Exemple #2
0
int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
                   uint8_t *pkt, uint32_t len, PacketQueue *pq)
{
    StatsIncr(tv, dtv->counter_eth);

    if (unlikely(len < ETHERNET_HEADER_LEN)) {
        ENGINE_SET_INVALID_EVENT(p, ETHERNET_PKT_TOO_SMALL);
        return TM_ECODE_FAILED;
    }

    if (unlikely(len > ETHERNET_HEADER_LEN + USHRT_MAX)) {
        return TM_ECODE_FAILED;
    }

    p->ethh = (EthernetHdr *)pkt;
    if (unlikely(p->ethh == NULL))
        return TM_ECODE_FAILED;

    SCLogDebug("p %p pkt %p ether type %04x", p, pkt, SCNtohs(p->ethh->eth_type));

    switch (SCNtohs(p->ethh->eth_type)) {
        case ETHERNET_TYPE_IP:
            //printf("DecodeEthernet ip4\n");
            DecodeIPV4(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                       len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_IPV6:
            //printf("DecodeEthernet ip6\n");
            DecodeIPV6(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                       len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_PPPOE_SESS:
            //printf("DecodeEthernet PPPOE Session\n");
            DecodePPPOESession(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                               len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_PPPOE_DISC:
            //printf("DecodeEthernet PPPOE Discovery\n");
            DecodePPPOEDiscovery(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                                 len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_VLAN:
        case ETHERNET_TYPE_8021QINQ:
            DecodeVLAN(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                                 len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_MPLS_UNICAST:
        case ETHERNET_TYPE_MPLS_MULTICAST:
            DecodeMPLS(tv, dtv, p, pkt + ETHERNET_HEADER_LEN,
                       len - ETHERNET_HEADER_LEN, pq);
            break;
        case ETHERNET_TYPE_DCE:
            if (unlikely(len < ETHERNET_DCE_HEADER_LEN)) {
                ENGINE_SET_INVALID_EVENT(p, DCE_PKT_TOO_SMALL);
            } else {
                DecodeEthernet(tv, dtv, p, pkt + ETHERNET_DCE_HEADER_LEN,
                    len - ETHERNET_DCE_HEADER_LEN, pq);
            }
            break;
        default:
            SCLogDebug("p %p pkt %p ether type %04x not supported", p,
                       pkt, SCNtohs(p->ethh->eth_type));
    }

    return TM_ECODE_OK;
}
Exemple #3
0
static void
DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
{
    SCEnter();

    uint8_t *orig_pkt = pkt;
    uint8_t nh = 0; /* careful, 0 is actually a real type */
    uint16_t hdrextlen = 0;
    uint16_t plen;
    char dstopts = 0;
    char exthdr_fh_done = 0;
    int hh = 0;
    int rh = 0;
    int eh = 0;
    int ah = 0;

    nh = IPV6_GET_NH(p);
    plen = len;

    while(1)
    {
        /* No upper layer, but we do have data. Suspicious. */
        if (nh == IPPROTO_NONE && plen > 0) {
            ENGINE_SET_EVENT(p, IPV6_DATA_AFTER_NONE_HEADER);
            SCReturn;
        }

        if (plen < 2) { /* minimal needed in a hdr */
            SCReturn;
        }

        switch(nh)
        {
            case IPPROTO_TCP:
                IPV6_SET_L4PROTO(p,nh);
                DecodeTCP(tv, dtv, p, pkt, plen, pq);
                SCReturn;

            case IPPROTO_UDP:
                IPV6_SET_L4PROTO(p,nh);
                DecodeUDP(tv, dtv, p, pkt, plen, pq);
                SCReturn;

            case IPPROTO_ICMPV6:
                IPV6_SET_L4PROTO(p,nh);
                DecodeICMPV6(tv, dtv, p, pkt, plen, pq);
                SCReturn;

            case IPPROTO_SCTP:
                IPV6_SET_L4PROTO(p,nh);
                DecodeSCTP(tv, dtv, p, pkt, plen, pq);
                SCReturn;

            case IPPROTO_ROUTING:
                IPV6_SET_L4PROTO(p,nh);
                hdrextlen = 8 + (*(pkt+1) * 8);  /* 8 bytes + length in 8 octet units */

                SCLogDebug("hdrextlen %"PRIu8, hdrextlen);

                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }

                if (rh) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_RH);
                    /* skip past this extension so we can continue parsing the rest
                     * of the packet */
                    nh = *pkt;
                    pkt += hdrextlen;
                    plen -= hdrextlen;
                    break;
                }

                rh = 1;
                IPV6_EXTHDR_SET_RH(p);

                uint8_t ip6rh_type = *(pkt + 2);
                if (ip6rh_type == 0) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_RH_TYPE_0);
                }
                p->ip6eh.rh_type = ip6rh_type;

                nh = *pkt;
                pkt += hdrextlen;
                plen -= hdrextlen;
                break;

            case IPPROTO_HOPOPTS:
            case IPPROTO_DSTOPTS:
            {
                IPV6OptHAO hao_s, *hao = &hao_s;
                IPV6OptRA ra_s, *ra = &ra_s;
                IPV6OptJumbo jumbo_s, *jumbo = &jumbo_s;
                uint16_t optslen = 0;

                IPV6_SET_L4PROTO(p,nh);
                hdrextlen =  (*(pkt+1) + 1) << 3;
                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }

                uint8_t *ptr = pkt + 2; /* +2 to go past nxthdr and len */

                /* point the pointers to right structures
                 * in Packet. */
                if (nh == IPPROTO_HOPOPTS) {
                    if (hh) {
                        ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_HH);
                        /* skip past this extension so we can continue parsing the rest
                         * of the packet */
                        nh = *pkt;
                        pkt += hdrextlen;
                        plen -= hdrextlen;
                        break;
                    }

                    hh = 1;

                    optslen = ((*(pkt + 1) + 1 ) << 3) - 2;
                }
                else if (nh == IPPROTO_DSTOPTS)
                {
                    if (dstopts == 0) {
                        optslen = ((*(pkt + 1) + 1 ) << 3) - 2;
                        dstopts = 1;
                    } else if (dstopts == 1) {
                        optslen = ((*(pkt + 1) + 1 ) << 3) - 2;
                        dstopts = 2;
                    } else {
                        ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_DH);
                        /* skip past this extension so we can continue parsing the rest
                         * of the packet */
                        nh = *pkt;
                        pkt += hdrextlen;
                        plen -= hdrextlen;
                        break;
                    }
                }

                if (optslen > plen) {
                    /* since the packet is long enough (we checked
                     * plen against hdrlen, the optlen must be malformed. */
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                    /* skip past this extension so we can continue parsing the rest
                     * of the packet */
                    nh = *pkt;
                    pkt += hdrextlen;
                    plen -= hdrextlen;
                    break;
                }
/** \todo move into own function to loaded on demand */
                uint16_t padn_cnt = 0;
                uint16_t other_cnt = 0;
                uint16_t offset = 0;
                while(offset < optslen)
                {
                    if (*ptr == IPV6OPT_PAD1)
                    {
                        padn_cnt++;
                        offset++;
                        ptr++;
                        continue;
                    }

                    if (offset + 1 >= optslen) {
                        ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                        break;
                    }

                    /* length field for each opt */
                    uint8_t ip6_optlen = *(ptr + 1);

                    /* see if the optlen from the packet fits the total optslen */
                    if ((offset + 1 + ip6_optlen) > optslen) {
                        ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                        break;
                    }

                    if (*ptr == IPV6OPT_PADN) /* PadN */
                    {
                        //printf("PadN option\n");
                        padn_cnt++;

                        /* a zero padN len would be weird */
                        if (ip6_optlen == 0)
                            ENGINE_SET_EVENT(p, IPV6_EXTHDR_ZERO_LEN_PADN);
                    }
                    else if (*ptr == IPV6OPT_RA) /* RA */
                    {
                        ra->ip6ra_type = *(ptr);
                        ra->ip6ra_len  = ip6_optlen;

                        if (ip6_optlen < sizeof(ra->ip6ra_value)) {
                            ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                            break;
                        }

                        memcpy(&ra->ip6ra_value, (ptr + 2), sizeof(ra->ip6ra_value));
                        ra->ip6ra_value = SCNtohs(ra->ip6ra_value);
                        //printf("RA option: type %" PRIu32 " len %" PRIu32 " value %" PRIu32 "\n",
                        //    ra->ip6ra_type, ra->ip6ra_len, ra->ip6ra_value);
                        other_cnt++;
                    }
                    else if (*ptr == IPV6OPT_JUMBO) /* Jumbo */
                    {
                        jumbo->ip6j_type = *(ptr);
                        jumbo->ip6j_len  = ip6_optlen;

                        if (ip6_optlen < sizeof(jumbo->ip6j_payload_len)) {
                            ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                            break;
                        }

                        memcpy(&jumbo->ip6j_payload_len, (ptr+2), sizeof(jumbo->ip6j_payload_len));
                        jumbo->ip6j_payload_len = SCNtohl(jumbo->ip6j_payload_len);
                        //printf("Jumbo option: type %" PRIu32 " len %" PRIu32 " payload len %" PRIu32 "\n",
                        //    jumbo->ip6j_type, jumbo->ip6j_len, jumbo->ip6j_payload_len);
                    }
                    else if (*ptr == IPV6OPT_HAO) /* HAO */
                    {
                        hao->ip6hao_type = *(ptr);
                        hao->ip6hao_len  = ip6_optlen;

                        if (ip6_optlen < sizeof(hao->ip6hao_hoa)) {
                            ENGINE_SET_EVENT(p, IPV6_EXTHDR_INVALID_OPTLEN);
                            break;
                        }

                        memcpy(&hao->ip6hao_hoa, (ptr+2), sizeof(hao->ip6hao_hoa));
                        //printf("HAO option: type %" PRIu32 " len %" PRIu32 " ",
                        //    hao->ip6hao_type, hao->ip6hao_len);
                        //char addr_buf[46];
                        //PrintInet(AF_INET6, (char *)&(hao->ip6hao_hoa),
                        //    addr_buf,sizeof(addr_buf));
                        //printf("home addr %s\n", addr_buf);
                        other_cnt++;
                    } else {
                        if (nh == IPPROTO_HOPOPTS)
                            ENGINE_SET_EVENT(p, IPV6_HOPOPTS_UNKNOWN_OPT);
                        else
                            ENGINE_SET_EVENT(p, IPV6_DSTOPTS_UNKNOWN_OPT);

                        other_cnt++;
                    }
                    uint16_t optlen = (*(ptr + 1) + 2);
                    ptr += optlen; /* +2 for opt type and opt len fields */
                    offset += optlen;
                }
                /* flag packets that have only padding */
                if (padn_cnt > 0 && other_cnt == 0) {
                    if (nh == IPPROTO_HOPOPTS)
                        ENGINE_SET_EVENT(p, IPV6_HOPOPTS_ONLY_PADDING);
                    else
                        ENGINE_SET_EVENT(p, IPV6_DSTOPTS_ONLY_PADDING);
                }

                nh = *pkt;
                pkt += hdrextlen;
                plen -= hdrextlen;
                break;
            }

            case IPPROTO_FRAGMENT:
            {
                IPV6_SET_L4PROTO(p,nh);
                /* store the offset of this extension into the packet
                 * past the ipv6 header. We use it in defrag for creating
                 * a defragmented packet without the frag header */
                if (exthdr_fh_done == 0) {
                    p->ip6eh.fh_offset = pkt - orig_pkt;
                    exthdr_fh_done = 1;
                }

                uint16_t prev_hdrextlen = hdrextlen;
                hdrextlen = sizeof(IPV6FragHdr);
                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }

                /* for the frag header, the length field is reserved */
                if (*(pkt + 1) != 0) {
                    ENGINE_SET_EVENT(p, IPV6_FH_NON_ZERO_RES_FIELD);
                    /* non fatal, lets try to continue */
                }

                if (IPV6_EXTHDR_ISSET_FH(p)) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_FH);
                    nh = *pkt;
                    pkt += hdrextlen;
                    plen -= hdrextlen;
                    break;
                }

                /* set the header flag first */
                IPV6_EXTHDR_SET_FH(p);

                /* parse the header and setup the vars */
                DecodeIPV6FragHeader(p, pkt, hdrextlen, plen, prev_hdrextlen);

                /* if FH has offset 0 and no more fragments are coming, we
                 * parse this packet further right away, no defrag will be
                 * needed. It is a useless FH then though, so we do set an
                 * decoder event. */
                if (p->ip6eh.fh_more_frags_set == 0 && p->ip6eh.fh_offset == 0) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_USELESS_FH);

                    nh = *pkt;
                    pkt += hdrextlen;
                    plen -= hdrextlen;
                    break;
                }

                /* the rest is parsed upon reassembly */
                p->flags |= PKT_IS_FRAGMENT;
                SCReturn;
            }
            case IPPROTO_ESP:
            {
                IPV6_SET_L4PROTO(p,nh);
                hdrextlen = sizeof(IPV6EspHdr);
                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }

                if (eh) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_EH);
                    SCReturn;
                }

                eh = 1;

                nh = IPPROTO_NONE;
                pkt += hdrextlen;
                plen -= hdrextlen;
                break;
            }
            case IPPROTO_AH:
            {
                IPV6_SET_L4PROTO(p,nh);
                /* we need the header as a minimum */
                hdrextlen = sizeof(IPV6AuthHdr);
                /* the payload len field is the number of extra 4 byte fields,
                 * IPV6AuthHdr already contains the first */
                if (*(pkt+1) > 0)
                    hdrextlen += ((*(pkt+1) - 1) * 4);

                SCLogDebug("hdrextlen %"PRIu8, hdrextlen);

                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }

                IPV6AuthHdr *ahhdr = (IPV6AuthHdr *)pkt;
                if (ahhdr->ip6ah_reserved != 0x0000) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_AH_RES_NOT_NULL);
                }

                if (ah) {
                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_AH);
                    nh = *pkt;
                    pkt += hdrextlen;
                    plen -= hdrextlen;
                    break;
                }

                ah = 1;

                nh = *pkt;
                pkt += hdrextlen;
                plen -= hdrextlen;
                break;
            }
            case IPPROTO_IPIP:
                IPV6_SET_L4PROTO(p,nh);
                DecodeIPv4inIPv6(tv, dtv, p, pkt, plen, pq);
                SCReturn;
            /* none, last header */
            case IPPROTO_NONE:
                IPV6_SET_L4PROTO(p,nh);
                SCReturn;
            case IPPROTO_ICMP:
                ENGINE_SET_EVENT(p,IPV6_WITH_ICMPV4);
                SCReturn;
            /* no parsing yet, just skip it */
            case IPPROTO_MH:
            case IPPROTO_HIP:
            case IPPROTO_SHIM6:
                hdrextlen = 8 + (*(pkt+1) * 8);  /* 8 bytes + length in 8 octet units */
                if (hdrextlen > plen) {
                    ENGINE_SET_EVENT(p, IPV6_TRUNC_EXTHDR);
                    SCReturn;
                }
                nh = *pkt;
                pkt += hdrextlen;
                plen -= hdrextlen;
                break;
            default:
                ENGINE_SET_EVENT(p, IPV6_UNKNOWN_NEXT_HEADER);
                IPV6_SET_L4PROTO(p,nh);
                SCReturn;
        }
    }

    SCReturn;
}