/**
 * \brief Compares 2 addresses(address ranges) and returns the relationship
 *        between the 2 addresses.
 *
 * \param a Pointer to the first address instance to be compared.
 * \param b Pointer to the second address instance to be compared.
 *
 * \retval ADDRESS_EQ If the 2 address ranges a and b, are equal.
 * \retval ADDRESS_ES b encapsulates a. b_ip1[...a_ip1...a_ip2...]b_ip2.
 * \retval ADDRESS_EB a encapsulates b. a_ip1[...b_ip1....b_ip2...]a_ip2.
 * \retval ADDRESS_LE a_ip1(...b_ip1==a_ip2...)b_ip2
 * \retval ADDRESS_LT a_ip1(...b_ip1...a_ip2...)b_ip2
 * \retval ADDRESS_GE b_ip1(...a_ip1==b_ip2...)a_ip2
 * \retval ADDRESS_GT a_ip1 > b_ip2, i.e. the address range for 'a' starts only
 *                    after the end of the address range for 'b'
 */
int DetectAddressCmpIPv4(DetectAddress *a, DetectAddress *b)
{
    uint32_t a_ip1 = SCNtohl(a->ip.addr_data32[0]);
    uint32_t a_ip2 = SCNtohl(a->ip2.addr_data32[0]);
    uint32_t b_ip1 = SCNtohl(b->ip.addr_data32[0]);
    uint32_t b_ip2 = SCNtohl(b->ip2.addr_data32[0]);

    if (a_ip1 == b_ip1 && a_ip2 == b_ip2) {
        SCLogDebug("ADDRESS_EQ");
        return ADDRESS_EQ;
    } else if (a_ip1 >= b_ip1 && a_ip1 <= b_ip2 && a_ip2 <= b_ip2) {
        SCLogDebug("ADDRESS_ES");
        return ADDRESS_ES;
    } else if (a_ip1 <= b_ip1 && a_ip2 >= b_ip2) {
        SCLogDebug("ADDRESS_EB");
        return ADDRESS_EB;
    } else if (a_ip1 < b_ip1 && a_ip2 < b_ip2 && a_ip2 >= b_ip1) {
        SCLogDebug("ADDRESS_LE");
        return ADDRESS_LE;
    } else if (a_ip1 < b_ip1 && a_ip2 < b_ip2) {
        SCLogDebug("ADDRESS_LT");
        return ADDRESS_LT;
    } else if (a_ip1 > b_ip1 && a_ip1 <= b_ip2 && a_ip2 > b_ip2) {
        SCLogDebug("ADDRESS_GE");
        return ADDRESS_GE;
    } else if (a_ip1 > b_ip2) {
        SCLogDebug("ADDRESS_GT");
        return ADDRESS_GT;
    } else {
        /* should be unreachable */
        SCLogDebug("Internal Error: should be unreachable");
    }

    return ADDRESS_ER;
}
/**
 * \brief Check if the address group list covers the complete IPv4 IP space.
 *
 * \param ag Pointer to a DetectAddress list head, which has to be checked to
 *           see if the address ranges in it, cover the entire IPv4 IP space.
 *
 * \retval 1 Yes, it covers the entire IPv4 address range.
 * \retval 0 No, it doesn't cover the entire IPv4 address range.
 */
int DetectAddressIsCompleteIPSpaceIPv4(DetectAddress *ag)
{
    uint32_t next_ip = 0;

    if (ag == NULL)
        return 0;

    /* if we don't start with 0.0.0.0 we know we're good */
    if (SCNtohl(ag->ip.addr_data32[0]) != 0x00000000)
        return 0;

    /* if we're ending with 255.255.255.255 while we know we started with
     * 0.0.0.0 it's the complete space */
    if (SCNtohl(ag->ip2.addr_data32[0]) == 0xFFFFFFFF)
        return 1;

    next_ip = htonl(SCNtohl(ag->ip2.addr_data32[0]) + 1);
    ag = ag->next;

    for ( ; ag != NULL; ag = ag->next) {

        if (ag->ip.addr_data32[0] != next_ip)
            return 0;

        if (SCNtohl(ag->ip2.addr_data32[0]) == 0xFFFFFFFF)
            return 1;

        next_ip = htonl(SCNtohl(ag->ip2.addr_data32[0]) + 1);
    }

    return 0;
}
/**
 * \brief Extends a target address range if the the source address range is
 *        wider than the target address range on either sides.
 *
 *        Every address is a range, i.e. address->ip1....address->ip2.  For
 *        example 1.2.3.4 to 192.168.1.1.
 *        if source->ip1 is smaller than target->ip1, it indicates that the
 *        source's left address limit is greater(range wise) than the target's
 *        left address limit, and hence we reassign the target's left address
 *        limit to source's left address limit.
 *        Similary if source->ip2 is greater than target->ip2, it indicates that
 *        the source's right address limit is greater(range wise) than the
 *        target's right address limit, and hence we reassign the target's right
 *        address limit to source's right address limit.
 *
 * \param de_ctx Pointer to the detection engine context.
 * \param target Pointer to the target DetectAddress instance that has to be
 *               updated.
 * \param source Pointer to the source DetectAddress instance that is used
 *               to decided whether we extend the target's address range.
 *
 * \retval  0 On success.
 * \retval -1 On failure.
 */
int DetectAddressJoinIPv4(DetectEngineCtx *de_ctx, DetectAddress *target,
                          DetectAddress *source)
{
    if (source == NULL || target == NULL)
        return -1;

    if (SCNtohl(source->ip.addr_data32[0]) < SCNtohl(target->ip.addr_data32[0]))
        target->ip.addr_data32[0] = source->ip.addr_data32[0];

    if (SCNtohl(source->ip2.addr_data32[0]) > SCNtohl(target->ip2.addr_data32[0]))
        target->ip2.addr_data32[0] = source->ip2.addr_data32[0];

    return 0;
}
/**
 * \brief Cuts and returns an address range, which is the complement of the
 *        address range that is supplied as the argument.
 *
 *        For example:
 *
 *        If a = 0.0.0.0-1.2.3.4,
 *            then a = 1.2.3.4-255.255.255.255 and b = NULL
 *        If a = 1.2.3.4-255.255.255.255,
 *            then a = 0.0.0.0-1.2.3.4 and b = NULL
 *        If a = 1.2.3.4-192.168.1.1,
 *            then a = 0.0.0.0-1.2.3.3 and b = 192.168.1.2-255.255.255.255
 *
 * \param a Pointer to an address range (DetectAddress) instance whose complement
 *          has to be returned in a and b.
 * \param b Pointer to DetectAddress pointer, that will be supplied back with a
 *          new DetectAddress instance, if the complement demands so.
 *
 * \retval  0 On success.
 * \retval -1 On failure.
 */
int DetectAddressCutNotIPv4(DetectAddress *a, DetectAddress **b)
{
    uint32_t a_ip1 = SCNtohl(a->ip.addr_data32[0]);
    uint32_t a_ip2 = SCNtohl(a->ip2.addr_data32[0]);
    DetectAddress *tmp_b = NULL;

    /* default to NULL */
    *b = NULL;

    if (a_ip1 != 0x00000000 && a_ip2 != 0xFFFFFFFF) {
        a->ip.addr_data32[0]  = htonl(0x00000000);
        a->ip2.addr_data32[0] = htonl(a_ip1 - 1);

        tmp_b = DetectAddressInit();
        if (tmp_b == NULL)
            goto error;

        tmp_b->ip.family = AF_INET;
        tmp_b->ip.addr_data32[0]  = htonl(a_ip2 + 1);
        tmp_b->ip2.addr_data32[0] = htonl(0xFFFFFFFF);
        *b = tmp_b;
    } else if (a_ip1 == 0x00000000 && a_ip2 != 0xFFFFFFFF) {
        a->ip.addr_data32[0] = htonl(a_ip2 + 1);
        a->ip2.addr_data32[0] = htonl(0xFFFFFFFF);
    } else if (a_ip1 != 0x00000000 && a_ip2 == 0xFFFFFFFF) {
        a->ip.addr_data32[0] = htonl(0x00000000);
        a->ip2.addr_data32[0] = htonl(a_ip1 - 1);
    } else {
        goto error;
     }

    return 0;

error:
    return -1;
}
Example #5
0
static inline
#endif
void DecodeIPV6FragHeader(Packet *p, uint8_t *pkt,
                          uint16_t hdrextlen, uint16_t plen,
                          uint16_t prev_hdrextlen)
{
    uint16_t frag_offset = (*(pkt + 2) << 8 | *(pkt + 3)) & 0xFFF8;
    int frag_morefrags   = (*(pkt + 2) << 8 | *(pkt + 3)) & 0x0001;

    p->ip6eh.fh_offset = frag_offset;
    p->ip6eh.fh_more_frags_set = frag_morefrags ? TRUE : FALSE;
    p->ip6eh.fh_nh = *pkt;

    uint32_t fh_id;
    memcpy(&fh_id, pkt+4, 4);
    p->ip6eh.fh_id = SCNtohl(fh_id);

    SCLogDebug("IPV6 FH: offset %u, mf %s, nh %u, id %u/%x",
            p->ip6eh.fh_offset,
            p->ip6eh.fh_more_frags_set ? "true" : "false",
            p->ip6eh.fh_nh,
            p->ip6eh.fh_id, p->ip6eh.fh_id);

    // store header offset, data offset
    uint16_t frag_hdr_offset = (uint16_t)(pkt - GET_PKT_DATA(p));
    uint16_t data_offset = (uint16_t)(frag_hdr_offset + hdrextlen);
    uint16_t data_len = plen - hdrextlen;

    p->ip6eh.fh_header_offset = frag_hdr_offset;
    p->ip6eh.fh_data_offset = data_offset;
    p->ip6eh.fh_data_len = data_len;

    /* if we have a prev hdr, store the type and offset of it */
    if (prev_hdrextlen) {
        p->ip6eh.fh_prev_hdr_offset = frag_hdr_offset - prev_hdrextlen;
    }

    SCLogDebug("IPV6 FH: frag_hdr_offset %u, data_offset %u, data_len %u",
            p->ip6eh.fh_header_offset, p->ip6eh.fh_data_offset,
            p->ip6eh.fh_data_len);
}
/**
 * \brief Cut groups and merge sigs
 *
 *       a = 1.2.3.4, b = 1.2.3.4-1.2.3.5
 *       must result in: a == 1.2.3.4, b == 1.2.3.5, c == NULL
 *
 *       a = 1.2.3.4, b = 1.2.3.3-1.2.3.5
 *       must result in: a == 1.2.3.3, b == 1.2.3.4, c == 1.2.3.5
 *
 *       a = 1.2.3.0/24 b = 1.2.3.128-1.2.4.10
 *       must result in: a == 1.2.3.0/24, b == 1.2.4.0-1.2.4.10, c == NULL
 *
 *       a = 1.2.3.4, b = 1.2.3.0/24
 *       must result in: a == 1.2.3.0-1.2.3.3, b == 1.2.3.4, c == 1.2.3.5-1.2.3.255
 *
 * \retval  0 On success.
 * \retval -1 On failure.
 */
int DetectAddressCutIPv4(DetectEngineCtx *de_ctx, DetectAddress *a,
                         DetectAddress *b, DetectAddress **c)
{
    uint32_t a_ip1 = SCNtohl(a->ip.addr_data32[0]);
    uint32_t a_ip2 = SCNtohl(a->ip2.addr_data32[0]);
    uint32_t b_ip1 = SCNtohl(b->ip.addr_data32[0]);
    uint32_t b_ip2 = SCNtohl(b->ip2.addr_data32[0]);
    DetectAddress *tmp = NULL;
    DetectAddress *tmp_c = NULL;
    int r = 0;

    /* default to NULL */
    *c = NULL;

    r = DetectAddressCmpIPv4(a, b);
    if (r != ADDRESS_ES && r != ADDRESS_EB && r != ADDRESS_LE && r != ADDRESS_GE) {
        SCLogDebug("we shouldn't be here");
        goto error;
    }

    /* get a place to temporary put sigs lists */
    tmp = DetectAddressInit();
    if (tmp == NULL)
        goto error;

    /* we have 3 parts: [aaa[abab)bbb]
     * part a: a_ip1 <-> b_ip1 - 1
     * part b: b_ip1 <-> a_ip2
     * part c: a_ip2 + 1 <-> b_ip2
     */
    if (r == ADDRESS_LE) {
        SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_LE");

        a->ip.addr_data32[0]  = htonl(a_ip1);
        a->ip2.addr_data32[0] = htonl(b_ip1 - 1);

        b->ip.addr_data32[0]  = htonl(b_ip1);
        b->ip2.addr_data32[0] = htonl(a_ip2);

        tmp_c = DetectAddressInit();
        if (tmp_c == NULL)
            goto error;

        tmp_c->ip.family = AF_INET;
        tmp_c->ip.addr_data32[0] = htonl(a_ip2 + 1);
        tmp_c->ip2.addr_data32[0] = htonl(b_ip2);
        *c = tmp_c;

    /* we have 3 parts: [bbb[baba]aaa]
     * part a: b_ip1 <-> a_ip1 - 1
     * part b: a_ip1 <-> b_ip2
     * part c: b_ip2 + 1 <-> a_ip2
     */
    } else if (r == ADDRESS_GE) {
        SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_GE");

        a->ip.addr_data32[0] = htonl(b_ip1);
        a->ip2.addr_data32[0] = htonl(a_ip1 - 1);

        b->ip.addr_data32[0] = htonl(a_ip1);
        b->ip2.addr_data32[0] = htonl(b_ip2);

        tmp_c = DetectAddressInit();
        if (tmp_c == NULL)
            goto error;

        tmp_c->ip.family = AF_INET;
        tmp_c->ip.addr_data32[0]  = htonl(b_ip2 + 1);
        tmp_c->ip2.addr_data32[0] = htonl(a_ip2);
        *c = tmp_c;

        /* we have 2 or three parts:
         *
         * 2 part: [[abab]bbb] or [bbb[baba]]
         * part a: a_ip1 <-> a_ip2
         * part b: a_ip2 + 1 <-> b_ip2
         *
         * part a: b_ip1 <-> a_ip1 - 1
         * part b: a_ip1 <-> a_ip2
         *
         * 3 part [bbb[aaa]bbb]
         * becomes[aaa[bbb]ccc]
         *
         * part a: b_ip1 <-> a_ip1 - 1
         * part b: a_ip1 <-> a_ip2
         * part c: a_ip2 + 1 <-> b_ip2
         */
    } else if (r == ADDRESS_ES) {
        SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_ES");

        if (a_ip1 == b_ip1) {
            SCLogDebug("DetectAddressCutIPv4: 1");

            a->ip.addr_data32[0] = htonl(a_ip1);
            a->ip2.addr_data32[0] = htonl(a_ip2);

            b->ip.addr_data32[0] = htonl(a_ip2 + 1);
            b->ip2.addr_data32[0] = htonl(b_ip2);

        } else if (a_ip2 == b_ip2) {
            SCLogDebug("DetectAddressCutIPv4: 2");

            a->ip.addr_data32[0]   = htonl(b_ip1);
            a->ip2.addr_data32[0] = htonl(a_ip1 - 1);

            b->ip.addr_data32[0]   = htonl(a_ip1);
            b->ip2.addr_data32[0] = htonl(a_ip2);

        } else {
            SCLogDebug("3");

            a->ip.addr_data32[0]   = htonl(b_ip1);
            a->ip2.addr_data32[0] = htonl(a_ip1 - 1);

            b->ip.addr_data32[0]   = htonl(a_ip1);
            b->ip2.addr_data32[0] = htonl(a_ip2);

            tmp_c = DetectAddressInit();
            if (tmp_c == NULL)
                goto error;

            tmp_c->ip.family = AF_INET;
            tmp_c->ip.addr_data32[0] = htonl(a_ip2 + 1);
            tmp_c->ip2.addr_data32[0] = htonl(b_ip2);
            *c = tmp_c;
        }
        /* we have 2 or three parts:
         *
         * 2 part: [[baba]aaa] or [aaa[abab]]
         * part a: b_ip1 <-> b_ip2
         * part b: b_ip2 + 1 <-> a_ip2
         *
         * part a: a_ip1 <-> b_ip1 - 1
         * part b: b_ip1 <-> b_ip2
         *
         * 3 part [aaa[bbb]aaa]
         * becomes[aaa[bbb]ccc]
         *
         * part a: a_ip1 <-> b_ip2 - 1
         * part b: b_ip1 <-> b_ip2
         * part c: b_ip2 + 1 <-> a_ip2
         */
    } else if (r == ADDRESS_EB) {
        SCLogDebug("DetectAddressCutIPv4: r == ADDRESS_EB");

        if (a_ip1 == b_ip1) {
            SCLogDebug("DetectAddressCutIPv4: 1");

            a->ip.addr_data32[0] = htonl(b_ip1);
            a->ip2.addr_data32[0] = htonl(b_ip2);

            b->ip.addr_data32[0] = htonl(b_ip2 + 1);
            b->ip2.addr_data32[0] = htonl(a_ip2);
        } else if (a_ip2 == b_ip2) {
            SCLogDebug("DetectAddressCutIPv4: 2");

            a->ip.addr_data32[0]   = htonl(a_ip1);
            a->ip2.addr_data32[0] = htonl(b_ip1 - 1);

            b->ip.addr_data32[0]   = htonl(b_ip1);
            b->ip2.addr_data32[0] = htonl(b_ip2);
        } else {
            SCLogDebug("DetectAddressCutIPv4: 3");

            a->ip.addr_data32[0] = htonl(a_ip1);
            a->ip2.addr_data32[0] = htonl(b_ip1 - 1);

            b->ip.addr_data32[0] = htonl(b_ip1);
            b->ip2.addr_data32[0] = htonl(b_ip2);

            tmp_c = DetectAddressInit();
            if (tmp_c == NULL)
                goto error;

            tmp_c->ip.family = AF_INET;
            tmp_c->ip.addr_data32[0] = htonl(b_ip2 + 1);
            tmp_c->ip2.addr_data32[0] = htonl(a_ip2);
            *c = tmp_c;
        }
    }

    if (tmp != NULL)
        DetectAddressFree(tmp);

    return 0;

error:
    if (tmp != NULL)
        DetectAddressFree(tmp);
    return -1;
}
Example #7
0
/**
 * \brief Read data from nfq message and setup Packet
 *
 * \note
 * In case of error, this function verdict the packet
 * to avoid skb to get stuck in kernel.
 */
static int NFQSetupPkt (Packet *p, struct nfq_q_handle *qh, void *data)
{
    struct nfq_data *tb = (struct nfq_data *)data;
    int ret;
    char *pktdata;
    struct nfqnl_msg_packet_hdr *ph;

    ph = nfq_get_msg_packet_hdr(tb);
    if (ph != NULL) {
        p->nfq_v.id = SCNtohl(ph->packet_id);
        //p->nfq_v.hw_protocol = SCNtohs(p->nfq_v.ph->hw_protocol);
        p->nfq_v.hw_protocol = ph->hw_protocol;
    }
    /* coverity[missing_lock] */
    p->nfq_v.mark = nfq_get_nfmark(tb);
    if (nfq_config.mode == NFQ_REPEAT_MODE) {
        if ((nfq_config.mark & nfq_config.mask) ==
                (p->nfq_v.mark & nfq_config.mask)) {
            int iter = 0;
            if (already_seen_warning < MAX_ALREADY_TREATED)
                SCLogInfo("Packet seems already treated by suricata");
            already_seen_warning++;
            do {
                ret = nfq_set_verdict(qh, p->nfq_v.id, NF_ACCEPT, 0, NULL);
            } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
            if (ret < 0) {
                SCLogWarning(SC_ERR_NFQ_SET_VERDICT,
                             "nfq_set_verdict of %p failed %" PRId32 ": %s",
                             p, ret, strerror(errno));
            }
            return -1 ;
        }
    }
    p->nfq_v.ifi  = nfq_get_indev(tb);
    p->nfq_v.ifo  = nfq_get_outdev(tb);
    p->nfq_v.verdicted = 0;

#ifdef NFQ_GET_PAYLOAD_SIGNED
    ret = nfq_get_payload(tb, &pktdata);
#else
    ret = nfq_get_payload(tb, (unsigned char **) &pktdata);
#endif /* NFQ_GET_PAYLOAD_SIGNED */
    if (ret > 0) {
        /* nfq_get_payload returns a pointer to a part of memory
         * that is not preserved over the lifetime of our packet.
         * So we need to copy it. */
        if (ret > 65536) {
            /* Will not be able to copy data ! Set length to 0
             * to trigger an error in packet decoding.
             * This is unlikely to happen */
            SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "NFQ sent too big packet");
            SET_PKT_LEN(p, 0);
        } else if (runmode_workers) {
            PacketSetData(p, (uint8_t *)pktdata, ret);
        } else {
            PacketCopyData(p, (uint8_t *)pktdata, ret);
        }
    } else if (ret ==  -1) {
        /* unable to get pointer to data, ensure packet length is zero.
         * This will trigger an error in packet decoding */
        SET_PKT_LEN(p, 0);
    }

    ret = nfq_get_timestamp(tb, &p->ts);
    if (ret != 0 || p->ts.tv_sec == 0) {
        memset (&p->ts, 0, sizeof(struct timeval));
        gettimeofday(&p->ts, NULL);
    }

    p->datalink = DLT_RAW;
    return 0;
}
Example #8
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;
}