diff -uNr snort-2.9.2.2-clean/configure.in snort-2.9.2.2/configure.in
--- snort-2.9.2.2-clean/configure.in	2012-03-17 22:59:12.000000000 +0100
+++ snort-2.9.2.2/configure.in	2012-05-12 20:58:03.844857685 +0200
@@ -1704,6 +1704,7 @@
 src/dynamic-preprocessors/ssl/Makefile   \
 src/dynamic-preprocessors/modbus/Makefile   \
 src/dynamic-preprocessors/dnp3/Makefile   \
+src/dynamic-preprocessors/ipv6/Makefile   \
 src/dynamic-preprocessors/rzb_saac/Makefile   \
 src/output-plugins/Makefile    \
 src/preprocessors/Makefile     \
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/Makefile.am snort-2.9.2.2/src/dynamic-preprocessors/ipv6/Makefile.am
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/Makefile.am	2012-05-12 20:58:22.853272639 +0200
@@ -0,0 +1,33 @@
+## $Id
+AUTOMAKE_OPTIONS=foreign no-dependencies
+
+INCLUDES = -I../include -I${srcdir}/../libs
+
+libdir = ${exec_prefix}/lib/snort_dynamicpreprocessor
+
+lib_LTLIBRARIES = libsf_ipv6_preproc.la
+
+libsf_ipv6_preproc_la_LDFLAGS = -shared -export-dynamic -module @XCCFLAGS@
+if SO_WITH_STATIC_LIB
+libsf_ipv6_preproc_la_LIBADD = ../libsf_dynamic_preproc.la
+else
+nodist_libsf_ipv6_preproc_la_SOURCES = \
+../include/sf_dynamic_preproc_lib.c \
+../include/sfPolicyUserData.c
+endif
+
+libsf_ipv6_preproc_la_SOURCES = \
+sf_ip.c \
+spp_ipv6.c \
+spp_ipv6_constants.h \
+spp_ipv6_data_structs.c \
+spp_ipv6_data_structs.h \
+spp_ipv6.h \
+spp_ipv6_parse.c \
+spp_ipv6_ruleopt.c
+
+EXTRA_DIST = 
+
+all-local: $(LTLIBRARIES)
+	$(MAKE) DESTDIR=`pwd`/../build install-libLTLIBRARIES
+
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/sf_ip.c snort-2.9.2.2/src/dynamic-preprocessors/ipv6/sf_ip.c
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/sf_ip.c	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/sf_ip.c	2012-05-12 20:58:22.787208307 +0200
@@ -0,0 +1,589 @@
+/*
+** Copyright (C) 1998-2012 Sourcefire, Inc.
+** Adam Keeton
+** Kevin Liu <kliu@sourcefire.com>
+**
+** $Id$
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License Version 2 as
+** published by the Free Software Foundation.  You may not use, modify or
+** distribute this program under any other version of the GNU General
+** Public License.
+**
+** This program 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.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Adam Keeton
+ * sf_ip.c
+ * 11/17/06
+ *
+ * Library for managing IP addresses of either v6 or v4 families.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h> /* For ceil */
+#include "sf_ip.h"
+
+/* For inet_pton */
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif  /* WIN32 */
+
+#if 0
+/* Support function .. but could see some external uses */
+static inline int sfip_length(sfip_t *ip) {
+    ARG_CHECK1(ip, 0);
+
+    if(sfip_family(ip) == AF_INET) return 4;
+    return 16;
+}
+#endif
+
+/* Support function */
+// note that an ip6 address may have a trailing dotted quad form
+// but that it always has at least 2 ':'s; furthermore there is
+// no valid ip4 format (including mask) with 2 ':'s
+// we don't have to figure out if the format is entirely legal
+// we just have to be able to tell correct formats apart
+static inline int sfip_str_to_fam(const char *str) {
+    const char* s;
+    ARG_CHECK1(str, 0);
+    s = strchr(str, (int)':');
+    if ( s && strchr(s+1, (int)':') ) return AF_INET6;
+    if ( strchr(str, (int)'.') ) return AF_INET;
+    return AF_UNSPEC;
+}
+
+/* Place-holder allocation incase we want to do something more indepth later */
+static inline sfip_t *_sfip_alloc() {
+    /* Note: using calloc here instead of SnortAlloc since the dynamic libs
+     * can't presently resolve SnortAlloc */
+    return (sfip_t*)calloc(sizeof(sfip_t), 1);
+}
+
+/* Masks off 'val' bits from the IP contained within 'ip' */
+static inline int sfip_cidr_mask(sfip_t *ip, int val) {
+    int i;
+    unsigned int mask = 0;
+    unsigned int *p;
+    int index = (int)ceil(val / 32.0) - 1;
+
+    ARG_CHECK1(ip, SFIP_ARG_ERR);
+
+    p = ip->ip32;
+
+    if( val < 0 ||
+        ((sfip_family(ip) == AF_INET6) && val > 128) ||
+        ((sfip_family(ip) == AF_INET) && val > 32) ) {
+        return SFIP_ARG_ERR;
+    }
+
+    /* Build the netmask by converting "val" into
+     * the corresponding number of bits that are set */
+    for(i = 0; i < 32- (val - (index * 32)); i++)
+        mask = (mask<<1) + 1;
+
+    p[index] = htonl((ntohl(p[index]) & ~mask));
+
+    index++;
+
+    /* 0 off the rest of the IP */
+    for( ; index<4; index++) p[index] = 0;
+
+    return SFIP_SUCCESS;
+}
+
+/* Allocate IP address from a character array describing the IP */
+sfip_t *sfip_alloc(const char *ip, SFIP_RET *status) {
+    SFIP_RET tmp;
+    sfip_t *ret;
+
+    if(!ip) {
+        if(status)
+            *status = SFIP_ARG_ERR;
+        return NULL;
+    }
+
+    if((ret = _sfip_alloc()) == NULL) {
+        if(status)
+            *status = SFIP_ALLOC_ERR;
+        return NULL;
+    }
+
+    if( (tmp = sfip_pton(ip, ret)) != SFIP_SUCCESS) {
+        if(status)
+            *status = tmp;
+
+        sfip_free(ret);
+        return NULL;
+    }
+
+    if(status)
+        *status = SFIP_SUCCESS;
+
+    return ret;
+}
+
+/* Allocate IP address from an array of 8 byte integers */
+sfip_t *sfip_alloc_raw(void *ip, int family, SFIP_RET *status) {
+    sfip_t *ret;
+
+    if(!ip) {
+        if(status)
+            *status = SFIP_ARG_ERR;
+        return NULL;
+    }
+
+    if((ret = _sfip_alloc()) == NULL) {
+        if(status)
+            *status = SFIP_ALLOC_ERR;
+        return NULL;
+    }
+
+    ret->bits = (family==AF_INET?32:128);
+    ret->family = family;
+    /* XXX Replace with appropriate "high speed" copy */
+    memcpy(ret->ip8, ip, ret->bits/8);
+
+    if(status)
+        *status = SFIP_SUCCESS;
+
+    return ret;
+}
+
+/* Support function for _netmask_str_to_bit_count */
+static inline int _count_bits(unsigned int val) {
+    unsigned int count;
+
+    for (count = 0; val; count++) {
+        val &= val - 1;
+    }
+
+    return count;
+}
+
+/* Support function for sfip_pton.  Used for converting a netmask string
+ * into a number of bits to mask off */
+static inline int _netmask_str_to_bit_count(char *mask, int family) {
+    uint32_t buf[4];
+    int bits, i, nBits, nBytes;
+    uint8_t* bytes = (uint8_t*)buf;
+
+    /* XXX
+     * Mask not validated.
+     * Only sfip_pton should be using this function, and using it safely.
+     * XXX */
+
+    if(inet_pton(family, mask, buf) < 1)
+        return -1;
+
+    bits =  _count_bits(buf[0]);
+
+    if(family == AF_INET6) {
+        bits += _count_bits(buf[1]);
+        bits += _count_bits(buf[2]);
+        bits += _count_bits(buf[3]);
+        nBytes = 16;
+    } else {
+        nBytes = 4;
+    }
+
+    // now make sure that only the most significant bits are set
+    nBits = bits;
+    for ( i = 0; i < nBytes; i++ ) {
+        if ( nBits >= 8 ) {
+            if ( bytes[i] != 0xff ) return -1;
+            nBits -= 8;
+
+        } else if ( nBits == 0 ) {
+            if ( bytes[i] != 0x00 ) return -1;
+
+        } else {
+            if ( bytes[i] != ((0xff00 >> nBits) & 0xff) ) return -1;
+            nBits = 0;
+        }
+    }
+    return bits;
+}
+
+/* Parses "src" and stores results in "dst" */
+SFIP_RET sfip_pton(const char *src, sfip_t *dst) {
+    char *mask;
+    char *sfip_buf;
+    char *ip;
+    int bits;
+
+    if(!dst || !src)
+        return SFIP_ARG_ERR;
+
+    if((sfip_buf = strdup(src)) == NULL)
+        return SFIP_ALLOC_ERR;
+
+    ip = sfip_buf;
+    dst->family = sfip_str_to_fam(src);
+
+    /* skip whitespace or opening bracket */
+    while(isspace((int)*ip) || (*ip == '[')) ip++;
+
+    /* check for and extract a mask in CIDR form */
+    if( (mask = strchr(ip, (int)'/')) != NULL ) {
+        /* NULL out this character so inet_pton will see the
+         * correct ending to the IP string */
+        char* end = mask++;
+        while ( (end > ip) && isspace((int)end[-1]) ) end--;
+        *end = 0;
+
+        while(isspace((int)*mask)) mask++;
+
+        /* verify a leading digit */
+        if(((dst->family == AF_INET6) && !isxdigit((int)*mask)) ||
+           ((dst->family == AF_INET) && !isdigit((int)*mask))) {
+            free(sfip_buf);
+            return SFIP_CIDR_ERR;
+        }
+
+        /* Check if there's a netmask here instead of the number of bits */
+        if(strchr(mask, (int)'.') || strchr(mask, (int)':'))
+            bits = _netmask_str_to_bit_count(mask, sfip_str_to_fam(mask));
+        else
+            bits = atoi(mask);
+    }
+    else if(
+            /* If this is IPv4, ia ':' may used specified to indicate a netmask */
+            ((dst->family == AF_INET) && (mask = strchr(ip, (int)':')) != NULL) ||
+
+            /* We've already skipped the leading whitespace, if there is more
+             * whitespace, then there's probably a netmask specified after it. */
+             (mask = strchr(ip, (int)' ')) != NULL
+    ) {
+        char* end = mask++;
+        while ( (end > ip) && isspace((int)end[-1]) ) end--;
+        *end = 0;  /* Now the IP will end at this point */
+
+        /* skip whitespace */
+        while(isspace((int)*mask)) mask++;
+
+        /* Make sure we're either looking at a valid digit, or a leading
+         * colon, such as can be the case with IPv6 */
+        if(((dst->family == AF_INET) && isdigit((int)*mask)) ||
+           ((dst->family == AF_INET6) && (isxdigit((int)*mask) || *mask == ':'))) {
+            bits = _netmask_str_to_bit_count(mask, sfip_str_to_fam(mask));
+        }
+        /* No netmask */
+        else {
+            if(dst->family == AF_INET) bits = 32;
+            else bits = 128;
+        }
+    }
+    /* No netmask */
+    else {
+        if(dst->family == AF_INET) bits = 32;
+        else bits = 128;
+    }
+
+    if(inet_pton(dst->family, ip, dst->ip8) < 1) {
+        free(sfip_buf);
+        return SFIP_INET_PARSE_ERR;
+    }
+
+    /* Store mask */
+    dst->bits = bits;
+
+    /* Apply mask */
+    if(sfip_cidr_mask(dst, bits) != SFIP_SUCCESS) {
+        free(sfip_buf);
+        return SFIP_INVALID_MASK;
+    }
+
+    free(sfip_buf);
+    return SFIP_SUCCESS;
+}
+
+/* Sets existing IP, "dst", to be source IP, "src" */
+SFIP_RET sfip_set_raw(sfip_t *dst, void *src, int family) {
+
+    ARG_CHECK3(dst, src, dst->ip32, SFIP_ARG_ERR);
+
+    dst->family = family;
+
+    if(family == AF_INET) {
+        dst->ip32[0] = *(uint32_t*)src;
+        memset(&dst->ip32[1], 0, 12);
+        dst->bits = 32;
+    } else if(family == AF_INET6) {
+        memcpy(dst->ip8, src, 16);
+        dst->bits = 128;
+    } else {
+        return SFIP_ARG_ERR;
+    }
+
+    return SFIP_SUCCESS;
+}
+
+/* Sets existing IP, "dst", to be source IP, "src" */
+SFIP_RET sfip_set_ip(sfip_t *dst, const sfip_t *src) {
+    ARG_CHECK2(dst, src, SFIP_ARG_ERR);
+
+    dst->family = src->family;
+    dst->bits = src->bits;
+    dst->ip32[0] = src->ip32[0];
+    dst->ip32[1] = src->ip32[1];
+    dst->ip32[2] = src->ip32[2];
+    dst->ip32[3] = src->ip32[3];
+
+    return SFIP_SUCCESS;
+}
+
+/* Obfuscates an IP
+ * Makes 'ip': ob | (ip & mask) */
+void sfip_obfuscate(sfip_t *ob, sfip_t *ip) {
+    unsigned int *ob_p, *ip_p;
+    int index, i;
+    unsigned int mask = 0;
+
+    if(!ob || !ip)
+        return;
+
+    ob_p = ob->ip32;
+    ip_p = ip->ip32;
+
+    /* Build the netmask by converting "val" into
+     * the corresponding number of bits that are set */
+    index = (int)ceil(ob->bits / 32.0) - 1;
+
+    for(i = 0; i < 32- (ob->bits - (index * 32)); i++)
+        mask = (mask<<1) + 1;
+
+    /* Note: The old-Snort obfuscation code uses !mask for masking.
+     * hence, this code uses the same algorithm as sfip_cidr_mask
+     * except the mask below is not negated. */
+    ip_p[index] = htonl((ntohl(ip_p[index]) & mask));
+
+    /* 0 off the start of the IP */
+    while ( index > 0 ) ip_p[--index] = 0;
+
+    /* OR remaining pieces */
+    ip_p[0] |= ob_p[0];
+    ip_p[1] |= ob_p[1];
+    ip_p[2] |= ob_p[2];
+    ip_p[3] |= ob_p[3];
+}
+
+
+/* Check if ip is contained within the network specified by net */
+/* Returns SFIP_EQUAL if so.
+ * XXX sfip_contains assumes that "ip" is
+ *      not less-specific than "net" XXX
+*/
+SFIP_RET sfip_contains(sfip_t *net, sfip_t *ip) {
+    unsigned int bits, mask, temp, i;
+    int net_fam, ip_fam;
+    unsigned int *p1, *p2;
+
+    /* SFIP_CONTAINS is returned here due to how IpAddrSetContains
+     * handles zero'ed IPs" */
+    ARG_CHECK2(net, ip, SFIP_CONTAINS);
+
+    bits = sfip_bits(net);
+    net_fam = sfip_family(net);
+    ip_fam = sfip_family(ip);
+
+    /* If the families are mismatched, check if we're really comparing
+     * an IPv4 with a mapped IPv4 (in IPv6) address. */
+    if(net_fam != ip_fam) {
+        if((net_fam != AF_INET) || !sfip_ismapped(ip))
+            return SFIP_ARG_ERR;
+
+        /* Both are really IPv4.  Only compare last 4 bytes of 'ip'*/
+        p1 = net->ip32;
+        p2 = &ip->ip32[3];
+
+        /* Mask off bits */
+        bits = 32 - bits;
+        temp = (ntohl(*p2) >> bits) << bits;
+
+        if(ntohl(*p1) == temp) return SFIP_CONTAINS;
+
+        return SFIP_NOT_CONTAINS;
+    }
+
+    p1 = net->ip32;
+    p2 = ip->ip32;
+
+    /* Iterate over each 32 bit segment */
+    for(i=0; i < bits/32 && i < 3; i++, p1++, p2++) {
+        if(*p1 != *p2)
+            return SFIP_NOT_CONTAINS;
+    }
+
+    mask = 32 - (bits - 32*i);
+    if ( mask == 32 ) return SFIP_CONTAINS;
+
+    /* At this point, there are some number of remaining bits to check.
+     * Mask the bits we don't care about off of "ip" so we can compare
+     * the ints directly */
+    temp = ntohl(*p2);
+    temp = (temp >> mask) << mask;
+
+    /* If p1 was setup correctly through this library, there is no need to
+     * mask off any bits of its own. */
+    if(ntohl(*p1) == temp)
+        return SFIP_CONTAINS;
+
+    return SFIP_NOT_CONTAINS;
+
+}
+
+void sfip_raw_ntop(int family, const void *ip_raw, char *buf, int bufsize)
+{
+    if(!ip_raw || !buf ||
+       (family != AF_INET && family != AF_INET6) ||
+       /* Make sure if it's IPv6 that the buf is large enough. */
+       /* Need atleast a max of 8 fields of 4 bytes plus 7 for colons in
+        * between.  Need 1 more byte for null. */
+       (family == AF_INET6 && bufsize < INET6_ADDRSTRLEN) ||
+       /* Make sure if it's IPv4 that the buf is large enough. */
+       /* 4 fields of 3 numbers, plus 3 dots and a null byte */
+       (family == AF_INET && bufsize < INET_ADDRSTRLEN) )
+    {
+        if(buf && bufsize > 0) buf[0] = 0;
+        return;
+    }
+
+#if defined(HAVE_INET_NTOP) && !defined(REG_TEST)
+    if (!inet_ntop(family, ip_raw, buf, bufsize))
+        snprintf(buf, bufsize, "ERROR");
+#else
+    /* 4 fields of at most 3 characters each */
+    if(family == AF_INET) {
+        int i;
+        uint8_t *p = (uint8_t*)ip_raw;
+
+        for(i=0; p < ((uint8_t*)ip_raw) + 4; p++) {
+            i += sprintf(&buf[i], "%d", *p);
+
+            /* If this is the last iteration, this could technically cause one
+             *  extra byte to be written past the end. */
+            if(i < bufsize && ((p + 1) < ((uint8_t*)ip_raw+4)))
+                buf[i] = '.';
+
+            i++;
+        }
+
+    /* Check if this is really just an IPv4 address represented as 6,
+     * in compatible format */
+#if 0
+    }
+    else if(!field[0] && !field[1] && !field[2]) {
+        unsigned char *p = (unsigned char *)(&ip->ip[12]);
+
+        for(i=0; p < &ip->ip[16]; p++)
+             i += sprintf(&buf[i], "%d.", *p);
+#endif
+    }
+    else {
+        int i;
+        uint16_t *p = (uint16_t*)ip_raw;
+
+        for(i=0; p < ((uint16_t*)ip_raw) + 8; p++) {
+            i += sprintf(&buf[i], "%04x", ntohs(*p));
+
+            /* If this is the last iteration, this could technically cause one
+             *  extra byte to be written past the end. */
+            if(i < bufsize && ((p + 1) < ((uint16_t*)ip_raw) + 8))
+                buf[i] = ':';
+
+            i++;
+        }
+    }
+#endif
+}
+
+void sfip_ntop(const sfip_t *ip, char *buf, int bufsize)
+{
+    if(!ip)
+    {
+        if(buf && bufsize > 0) buf[0] = 0;
+        return;
+    }
+
+    sfip_raw_ntop(sfip_family(ip), ip->ip32, buf, bufsize);
+}
+
+/* Uses a static buffer to return a string representation of the IP */
+char *sfip_to_str(const sfip_t *ip)
+{
+    static char buf[INET6_ADDRSTRLEN];
+
+    sfip_ntop(ip, buf, sizeof(buf));
+
+    return buf;
+}
+
+void sfip_free(sfip_t *ip) {
+    if(ip) free(ip);
+}
+
+/* Returns 1 if the IP is non-zero. 0 otherwise */
+int sfip_is_loopback(sfip_t *ip) {
+    unsigned int *p;
+
+    ARG_CHECK1(ip, 0);
+
+    if(sfip_family(ip) == AF_INET) {
+        // 127.0.0.0/8 is IPv4 loopback
+        return (ip->ip8[0] == 0x7f);
+    }
+
+    p = ip->ip32;
+
+    /* Check the first 64 bits in an IPv6 address, and */
+    /* verify they're zero.  If not, it's not a loopback */
+    if(p[0] || p[1]) return 0;
+
+    /* Check if the 3rd 32-bit int is zero */
+    if ( p[2] == 0 ) {
+        /* ::7f00:0/104 is ipv4 compatible ipv6 */
+        /* ::1 is the IPv6 loopback */
+        return ( (ip->ip8[12] == 0x7f) || (ntohl(p[3]) == 0x1) );
+    }
+    /* Check the 3rd 32-bit int for a mapped IPv4 address */
+    if ( ntohl(p[2]) == 0xffff ) {
+        /* ::ffff:127.0.0.0/104 is IPv4 loopback mapped over IPv6 */
+        return ( ip->ip8[12] == 0x7f );
+    }
+    return 0;
+}
+
+int sfip_ismapped(sfip_t *ip) {
+    unsigned int *p;
+
+    ARG_CHECK1(ip, 0);
+
+    if(sfip_family(ip) == AF_INET)
+        return 0;
+
+    p = ip->ip32;
+
+    if(p[0] || p[1] || (ntohl(p[2]) != 0xffff && p[2] != 0)) return 0;
+
+    return 1;
+}
+
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6.c snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6.c
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6.c	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6.c	2012-05-12 20:58:22.793266356 +0200
@@ -0,0 +1,655 @@
+/*
+ * spp_ipv6.c
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Description:
+ * The IPv6 Preprocessor.
+ *
+ */
+
+#include "sf_ip.h"
+// for sf_ip.c:
+//#define INLINE inline
+//#include "sf_ip.c"
+
+#include "spp_ipv6.h"
+#include "spp_ipv6_constants.h"
+#include "spp_ipv6_data_structs.h"
+#include "spp_ipv6_data_structs.c"
+#include "spp_ipv6_ruleopt.c"
+
+/* snort boilerplate code to support contexts and profiling */
+tSfPolicyUserContextId ipv6_config = NULL;
+#ifdef SNORT_RELOAD
+tSfPolicyUserContextId ipv6_swap_config = NULL;
+#endif
+extern DynamicPreprocessorData _dpd;
+
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats ipv6PerfStats;
+#endif
+
+#include "spp_ipv6_parse.c"
+
+/**
+ * Register init functions when library is loaded.
+ */
+void IPv6_Preproc_Setup(void)
+{
+#ifndef SNORT_RELOAD
+    _dpd.registerPreproc("ipv6", IPv6_Init);
+#else
+    _dpd.registerPreproc("ipv6", IPv6_Init, NULL, NULL, NULL);
+#endif
+}
+
+/**
+ * Reset Stats
+ * (only used when reading multiple PCAP files, cf. README.pcap_readmode)
+ */
+static void IPv6_ResetStats(int signal, void *foo)
+{
+    struct IPv6_State *context;
+    sfPolicyUserPolicySet(ipv6_config, _dpd.getRuntimePolicy());
+    context = (struct IPv6_State *) sfPolicyUserDataGetCurrent(ipv6_config);
+
+    memset(context->stat, 0, sizeof(struct IPv6_Statistics));
+}
+
+/**
+ * Print some statistics on snort exit.
+ */
+static void IPv6_PrintStats(int exiting)
+{
+    struct IPv6_State *context;
+    sfPolicyUserPolicySet(ipv6_config, _dpd.getRuntimePolicy());
+    context = (struct IPv6_State *) sfPolicyUserDataGetCurrent(ipv6_config);
+    if (!context) return;
+
+    _dpd.logMsg("IPv6 statistics:\n");
+    _dpd.logMsg("% 10u seen Packets\n",      context->stat->pkt_seen);
+    _dpd.logMsg("% 10u invalid Packets\n",   context->stat->pkt_invalid);
+    _dpd.logMsg("% 10u Fragments\n",         context->stat->pkt_fragments);
+    _dpd.logMsg("% 10u IPv6\n",              context->stat->pkt_ip6h);
+    _dpd.logMsg("% 10u ICMPv6\n",            context->stat->pkt_icmpv6);
+    _dpd.logMsg("% 10u Other Upper Layer\n", context->stat->pkt_other);
+    _dpd.logMsg("\n");
+
+    _dpd.logMsg("% 10u router solicitation\n",    context->stat->pkt_icmp_rsol);
+    _dpd.logMsg("% 10u router announcement\n",    context->stat->pkt_icmp_radv);
+    _dpd.logMsg("% 10u neighbour solicitation\n", context->stat->pkt_icmp_nsol);
+    _dpd.logMsg("% 10u neighbour announcement\n", context->stat->pkt_icmp_nadv);
+    _dpd.logMsg("% 10u Mcast query\n",            context->stat->pkt_icmp_mlquery);
+    _dpd.logMsg("% 10u Mcast report\n",           context->stat->pkt_icmp_mlreport);
+    _dpd.logMsg("% 10u dst unreachable\n",        context->stat->pkt_icmp_unreach);
+    _dpd.logMsg("% 10u Other\n",                  context->stat->pkt_icmp_other);
+
+    _dpd.logMsg("\nAll routers (%d entries):\n", context->routers->entry_counter);
+    state_host_printlist(context->routers);
+
+    _dpd.logMsg("\nAll hosts (%d entries):\n", context->hosts->entry_counter);
+    state_host_printlist(context->hosts);
+
+    _dpd.logMsg("\nAll hosts in DAD state (%d entries):\n", context->unconfirmed->entry_counter);
+    state_host_printlist(context->unconfirmed);
+
+    size_t size = 0;
+    size_t total = 0;
+    total += sizeof (*context);
+    total += sizeof (*context->stat);
+    total += sizeof (*context->config);
+    _dpd.logMsg("\n\nlast memory usage\n\t is %6d bytes fix\n", total);
+
+    size = state_host_memusage(context->routers);
+    _dpd.logMsg("\tand %6d bytes for routers\n", size);
+    total += size;
+    size = state_host_memusage(context->hosts);
+    _dpd.logMsg("\tand %6d bytes for hosts\n", size);
+    total += size;
+    size = state_host_memusage(context->unconfirmed);
+    _dpd.logMsg("\tand %6d bytes for unconfirmed\n", size);
+    total += size;
+
+    _dpd.logMsg("\t==> %6d bytes total (IPv6_Host size: %d bytes)\n",
+                total, sizeof(struct IPv6_Host));
+}
+
+/**
+ * Initialization function, invoked when preprocessor is activated.
+ *
+ * Has to parse our configuration options,  add preprocessing callbacks,
+ * and init data structures.
+ */
+static void IPv6_Init(char *args)
+{
+    struct IP_List_head    *prefixwl;
+    struct MAC_Entry_head  *routerwl;
+    struct MAC_Entry_head  *hostwl;
+    struct IPv6_Hosts_head *routers;
+    struct IPv6_Hosts_head *hosts;
+    struct IPv6_Hosts_head *unconf;
+    struct IPv6_Statistics *stat;
+    struct IPv6_State      *context;
+    struct IPv6_Config     *config;
+
+    if (ipv6_config == NULL) {
+        ipv6_config = sfPolicyConfigCreate();
+    }
+
+    // allocate everything
+    prefixwl = (struct IP_List_head *)    calloc(1, sizeof (struct IP_List_head));
+    routerwl = (struct MAC_Entry_head *)  calloc(1, sizeof (struct MAC_Entry_head));
+    hostwl   = (struct MAC_Entry_head *)  calloc(1, sizeof (struct MAC_Entry_head));
+    routers  = (struct IPv6_Hosts_head *) calloc(1, sizeof (struct IPv6_Hosts_head));
+    hosts    = (struct IPv6_Hosts_head *) calloc(1, sizeof (struct IPv6_Hosts_head));
+    unconf   = (struct IPv6_Hosts_head *) calloc(1, sizeof (struct IPv6_Hosts_head));
+    stat     = (struct IPv6_Statistics *) calloc(1, sizeof (struct IPv6_Statistics));
+    config   = (struct IPv6_Config *)     calloc(1, sizeof (struct IPv6_Config));
+    context  = (struct IPv6_State *)      calloc(1, sizeof (struct IPv6_State));
+    if (!routerwl || !hostwl || !prefixwl || !routers || !hosts || !unconf
+        || !stat || !config || !context || !ipv6_config)
+        _dpd.fatalMsg("Could not allocate IPv6 dyn-pp configuration struct.\n");
+
+    // initialize
+    STAILQ_INIT(prefixwl);
+    RB_INIT(&routerwl->data);
+    RB_INIT(&hostwl->data);
+    RB_INIT(&routers->data);
+    RB_INIT(&hosts->data);
+    RB_INIT(&unconf->data);
+
+    config->router_whitelist = routerwl;
+    config->host_whitelist   = hostwl;
+    config->prefix_whitelist = prefixwl;
+
+    IPv6_Parse(args, config);
+    context->config  = config;
+    context->stat    = stat;
+    
+    context->routers     = routers;
+    context->hosts       = hosts;
+    context->unconfirmed = unconf;
+    context->routers->entry_limit     = context->config->max_routers;
+    context->hosts->entry_limit       = context->config->max_hosts;
+    context->unconfirmed->entry_limit = context->config->max_unconfirmed;
+
+    sfPolicyUserPolicySet(ipv6_config, _dpd.getParserPolicy());
+    sfPolicyUserDataSetCurrent(ipv6_config, context);
+
+    /* Register the preprocessor function, priority, ICMP, ID PP_IPv6
+     * -- we use PRIORITY_NORMALIZE because it's ok to run after frag3
+     * and receive only reassembled packets.
+     * Change to PRIORITY_FIRST if any checks have to see fragments as well.
+     */
+    _dpd.addPreproc(IPv6_Process, PRIORITY_NORMALIZE, PP_IPv6, PROTO_BIT__ICMP);
+    //_dpd.addPreproc(IPv6_Process, PRIORITY_FIRST, PP_IPv6, PROTO_BIT__ALL);
+
+    _dpd.addPreprocResetStats(IPv6_ResetStats, NULL, PRIORITY_FIRST, PP_IPv6);
+    _dpd.registerPreprocStats("ipv6", IPv6_PrintStats);
+#ifdef PERF_PROFILING
+    _dpd.addPreprocProfileFunc("ipv6", (void *)&ipv6PerfStats, 0, _dpd.totalPerfStats);
+#endif
+
+    // and now all rule options:
+    _dpd.preprocOptRegister("ipv", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_exthdr", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_extnum", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_flow", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_tclass", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_option", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_optval", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_rh", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("ip6_ext_ordered", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("icmp6_nd", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+    _dpd.preprocOptRegister("icmp6_nd_option", IPv6_Rule_Init, IPv6_Rule_Eval,
+            free, IPv6_Rule_Hash, IPv6_Rule_KeyCompare, NULL, NULL);
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Preprocessor: IPv6 is initialized\n"););
+}
+
+/**
+ * Count packet in statistics.
+ *
+ * @param p current packet
+ * @param stat my statistics
+ *
+ * @return void function
+ */
+inline static void IPv6_UpdateStats(const SFSnortPacket *p, struct IPv6_Statistics *stat)
+{
+    /* IPv6_Process already handles
+     *      pkt_seen and pkt_invalid
+     * and aborts if not pkt_ip6h
+     */
+    stat->pkt_ip6h++;
+
+    if (p->icmp_header) {
+        switch(p->icmp_header->type) {
+        case ICMP6_SOLICITATION:
+            stat->pkt_icmp_rsol++;
+            break;
+        case ICMP6_N_SOLICITATION:
+            stat->pkt_icmp_nsol++;
+            break;
+        case ICMP6_ADVERTISEMENT:
+            stat->pkt_icmp_radv++;
+            break;
+        case ICMP6_N_ADVERTISEMENT:
+            stat->pkt_icmp_nadv++;
+            break;
+        case ICMP6_UNREACH:
+            stat->pkt_icmp_unreach++;
+            break;
+        case ICMP6_MEMBERSHIP_QUERY:
+            stat->pkt_icmp_mlquery++;
+            break;
+        case ICMP6_MEMBERSHIP_REPORT:
+        case MLDV2_LISTENER_REPORT:
+            stat->pkt_icmp_mlreport++;
+            break;
+        default:
+            stat->pkt_icmp_other++;
+            break;
+        }
+    }
+}
+
+/* simple stateless checks of extension headers */
+inline static void IPv6_Process_Extensions(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    uint_fast8_t i;
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGBASE,
+            "IPv6_Process_Extensions() ext num = %d\n",
+            p->num_ip6_extensions););
+    for(i = 0; i < p->num_ip6_extensions; i++) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGBASE,
+            "IPv6_Process_Extensions() ext type = %d\n",
+            p->ip6_extensions[i].option_type););
+
+        if (p->ip6_extensions[i].option_type != IPPROTO_HOPOPTS
+            && p->ip6_extensions[i].option_type != IPPROTO_DSTOPTS) {
+            continue;
+        } else {
+            /* hbh_hdr   is the Ext Hdr with type and length
+             * hbh_hdr+1 is the first option with type and length
+             * cursor    iterates over the options
+             */
+            struct ip6_hbh *hbh_hdr = (struct ip6_hbh*) p->ip6_extensions[i].option_data;
+            u_int16_t ext_len = (hbh_hdr->ip6h_len + 1) << 3;
+            u_int8_t *cursor  = (u_int8_t *) (hbh_hdr+1);
+            u_int8_t *ext_end = ((u_int8_t *) hbh_hdr) + ext_len;
+            bool only_padding = true;
+
+            while (cursor < ext_end) {
+                struct ip6_opt *opt = (struct ip6_opt*) cursor;
+                switch (opt->ip6o_type) {
+                case 0: // Pad1
+                    cursor += 1;
+                    break;
+                case 1: // PadN
+                    cursor += 2 + opt->ip6o_len;
+                    break;
+                default: // everything else
+                    cursor += 2 + opt->ip6o_len;
+                    only_padding = false;
+                    break;
+                }
+            }
+            if (cursor != ext_end)
+                ALERT(SID_IP6_OPTION_LENGTH_ERR);
+            if (only_padding)
+                ALERT(SID_IP6_ONLY_PADDING_EXT);
+        }
+    }
+}
+
+/**
+ * Processing callback function for all packets.
+ *
+ * @param p packet to detect anomalies and overwrite attacks on
+ * @param context unused
+ *
+ * @return void function
+ */
+void IPv6_Process(void *pkt, void *snortcontext)
+{
+    PROFILE_VARS;
+    SFSnortPacket *p = (SFSnortPacket *) pkt;
+    struct IPv6_State *context;
+
+    sfPolicyUserPolicySet(ipv6_config, _dpd.getRuntimePolicy());
+    context = (struct IPv6_State *) sfPolicyUserDataGetCurrent(ipv6_config);
+
+    //DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "IPv6_Process() called, pkt type = %d\n",
+        //(p && p->ip6h) ? p->ip6h->next : 0););
+
+    context->stat->pkt_seen++;
+    /* is the packet and the configuration valid? */
+    if ((p == NULL) || (context == NULL)) {
+        context->stat->pkt_invalid++;
+        return;
+    } else if (!p->ip6h) {
+        return;
+    } else if (p->ip_fragmented) { /* skip incomplete packets */
+        context->stat->pkt_fragments++;
+        return;
+    }
+    // else
+
+    PREPROC_PROFILE_START(ipv6PerfStats);
+    IPv6_UpdateStats(p, context->stat);
+    if (p->ip6h->next != IPPROTO_ICMPV6) {
+        context->stat->pkt_other++;
+    } else {
+        context->stat->pkt_icmpv6++;
+
+        IPv6_Process_Extensions(p, context);
+        
+        if (ND_hdrlen[p->icmp_header->type]) {
+            IPv6_Process_ND_Options(p, context);
+        }
+        if (p->icmp_header->type == ICMP6_ADVERTISEMENT) {
+            IPv6_Process_ICMPv6_RA_stateless(p);
+        }
+        if (context->config->track_ndp) {
+            IPv6_Process_ICMPv6(p, context);
+        }
+    }
+    PREPROC_PROFILE_END(ipv6PerfStats);
+}
+
+/**
+ * Check ICMPv6 ND Options.
+ * icmp_hdr_len is givenby caller, because it varies depending on msg type
+ */
+inline static void IPv6_Process_ND_Options(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    size_t icmp_hdr_len = ND_hdrlen[p->icmp_header->type];
+    size_t len = p->ip_payload_size - icmp_hdr_len;
+    const u_int8_t *ptr = p->ip_payload + icmp_hdr_len;
+    struct nd_opt_hdr *option = (struct nd_opt_hdr *) ptr;
+
+    while (len) {
+        uint_fast8_t optlen = 8 * (option->nd_opt_len);
+        if (option->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+            && memcmp(p->ether_header->ether_source, option + 1, MAC_LENGTH))
+            ALERT(SID_ICMP6_LINKADDR_MISMATCH);
+ 
+        if (optlen > len) {
+            // should this be an alert of its own?
+            _dpd.logMsg("IPv6 decoder problem. malformed ND option lenghts.");
+            break;
+        }
+        len -= optlen;
+        option = (struct nd_opt_hdr *) ((u_int8_t*) option + (8 * option->nd_opt_len));
+    }
+}
+
+/**
+ * Process an IPv6 ICMP packet.
+ */
+static void IPv6_Process_ICMPv6(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    // safety check
+    if (!p || !p->icmp_header) {
+        _dpd.logMsg("IPv6 decoder problem. ICMP packet without struct icmp_header");
+        return;
+    }
+
+    /* check if ongoing DAD */
+    struct IPv6_Host *ip_entry;
+    ip_entry = get_host_entry(context->unconfirmed, &(p->ip6h->ip_dst));
+    if (ip_entry) {
+        ip_entry->type.dad.contacted++;
+    }
+
+    /* check type */
+    switch (p->icmp_header->type) {
+    case ICMP6_ADVERTISEMENT:
+        IPv6_Process_ICMPv6_RA(p, context);
+        break;
+    case ICMP6_N_ADVERTISEMENT:
+        IPv6_Process_ICMPv6_NA(p, context);
+        break;
+    case ICMP6_N_SOLICITATION:
+        IPv6_Process_ICMPv6_NS(p, context);
+        break;
+    default:
+        break;
+    }
+
+    /* periodically expire old entries from state */
+    if (p->pkt_header->ts.tv_sec >= context->next_expire) {
+        //DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "%s expire state\n", pprint_ts(p->pkt_header->ts.tv_sec)););
+        context->next_expire = p->pkt_header->ts.tv_sec + context->config->expire_run_interval;
+        state_host_expirelist(context->routers,     p->pkt_header->ts.tv_sec, context->config->keep_state_duration);
+        state_host_expirelist(context->hosts,       p->pkt_header->ts.tv_sec, context->config->keep_state_duration);
+        state_host_expirelist(context->unconfirmed, p->pkt_header->ts.tv_sec, context->config->keep_state_duration);
+    };
+
+}
+
+/**
+ * Process an IPv6 RA packet stateless, ie. check lifetime.
+ */
+static void IPv6_Process_ICMPv6_RA_stateless(const SFSnortPacket *p)
+{
+    struct ICMPv6_RA *radv = (struct ICMPv6_RA*) p->ip_payload;
+
+    if (radv->nd_ra_lifetime == 0) {
+        ALERT(SID_ICMP6_RA_LIFETIME0);
+    }
+}
+
+/**
+ * Process an IPv6 RA packet.
+ */
+static void IPv6_Process_ICMPv6_RA(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    struct IPv6_Host *hostentry;
+    struct ICMPv6_RA *radv;
+    struct nd_opt_hdr *option;
+    struct nd_opt_prefix_info *prefix_info;
+    sfip_t *prefix = NULL;
+    uint_fast16_t len = p->ip_payload_size;
+    LISTOP_RET rc;
+    SFIP_RET sfip_rc;
+
+    radv   = (struct ICMPv6_RA*) p->ip_payload;
+    option = (struct nd_opt_hdr *) (radv + 1);
+    len -= sizeof (struct ICMPv6_RA);
+
+    while (len) {
+        // check some known options
+        switch (option->nd_opt_type) {
+        case ND_OPT_PREFIX_INFORMATION:
+            prefix_info = (struct nd_opt_prefix_info *) option;
+            prefix = sfip_alloc_raw(&prefix_info->nd_opt_pi_prefix, AF_INET6, &sfip_rc);
+            if (sfip_rc != SFIP_SUCCESS) {
+                _dpd.errMsg("sfip_alloc_raw() failed\n");
+                return;
+            }
+            sfip_set_bits(prefix, prefix_info->nd_opt_pi_prefix_len);
+            if (context->config->report_prefix_change && !ip_inlist(context->config->prefix_whitelist, prefix)) {
+                DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "IP prefix %s/%d not in "
+                    "configured list\n", sfip_to_str(prefix), sfip_bits(prefix)););
+                ALERT(SID_ICMP6_RA_UNKNOWN_PREFIX);
+            }
+
+            break;
+        default:
+            break;
+        }
+        len -= 8 * (option->nd_opt_len);
+        option = (struct nd_opt_hdr *) ((u_int8_t*) option + (8 * option->nd_opt_len));
+    }
+    // add state
+    rc = state_router_add(context->routers,
+                     &hostentry,
+                     &p->pkt_header->ts,
+                     p->ether_header->ether_source,
+                     &p->ip6h->ip_src,
+                     prefix, radv);
+
+    if (rc == LISTOP_ADDED) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "new IPv6 router advertised: %s / %s\n",
+                      pprint_mac(p->ether_header->ether_source),
+                      sfip_to_str(&p->ip6h->ip_src)););
+
+        // different events, depending on existing whitelist
+        if (context->config->router_whitelist
+         && !RB_EMPTY(&context->config->router_whitelist->data)
+         && !get_mac_entry(context->config->router_whitelist,
+                           p->ether_header->ether_source)) {
+            ALERT(SID_ICMP6_INVALID_ROUTER_MAC);
+        } else {
+            ALERT(SID_ICMP6_RA_NEW_ROUTER);
+        }
+    }
+}
+
+/**
+ * Process neighbour advertisement msgs
+ */
+static void IPv6_Process_ICMPv6_NA(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    /* check if part of DAD */
+    struct nd_neighbor_advert *na = (struct nd_neighbor_advert *) p->ip_payload;
+    SFIP_RET sfrc;
+    sfip_t *target_ip;
+    struct IPv6_Host *ip_entry;
+
+    target_ip = sfip_alloc_raw(&na->nd_na_target, AF_INET6, &sfrc);
+    if (sfrc != SFIP_SUCCESS) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "sfip_alloc_raw failed in %s:%d\n", __FILE__, __LINE__););
+        return;
+    };
+
+    ip_entry = get_host_entry(context->unconfirmed, target_ip);
+    if (!ip_entry) {
+        /* IP is yet unknown --> put into DAD state */
+        ip_entry = create_dad_entry_ifnew(context->unconfirmed,
+                                           &p->pkt_header->ts,
+                                           p->ether_header->ether_source,
+                                           target_ip);
+        /* no DAD info, so simply trust NA
+         *
+         * TODO: this leads to new DoS opportunity
+         *  --> only confirm after some other communication occurs (TCP, UDP or MLD)
+
+        confirm_host(p, context, target_ip);
+         */
+        return;
+    }
+
+    /* current DAD for this IP, now check details */
+    if (!memcmp(p->ether_header->ether_source, ip_entry->ether_source, MAC_LENGTH)) {
+        /* MAC matches -- same node */
+        if (ip_entry->type.dad.contacted) {
+            /* host entered the network */
+            // possible optimization: keep IPv6_Host object to save free/malloc
+            confirm_host(p, context, target_ip);
+            del_dad_entry(context->unconfirmed, ip_entry);
+        }
+        /* else DAD exists, but still unconfirmed, so do nothing
+         *  (could be result of NA flood/spoof) */
+    } else {
+        /* MAC does not match -- collision
+         * --> check if NA from known MAC (legitimate) or not (suspicious) */
+        if (get_host_entry(context->hosts, target_ip)
+            || get_host_entry(context->routers, target_ip)
+            || get_mac_entry(context->config->host_whitelist, p->ether_header->ether_source)
+            || get_mac_entry(context->config->router_whitelist, p->ether_header->ether_source)) {
+            // looks legitimate
+            DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "DAD collision with host %s / %s\n",
+                          pprint_mac(p->ether_header->ether_source),
+                          sfip_to_str(target_ip)););
+            ALERT(SID_ICMP6_DAD_COLLISION);
+        } else {
+            // never seen the 2nd host before --> probably an attack
+            DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "DAD DoS from (prob. fake MAC) %s / %s\n",
+                          pprint_mac(p->ether_header->ether_source),
+                          sfip_to_str(target_ip)););
+            ALERT(SID_ICMP6_DAD_DOS);
+        }
+    }
+}
+
+/**
+ * Process neighbour solicitation msgs
+ */
+static void IPv6_Process_ICMPv6_NS(const SFSnortPacket *p, struct IPv6_State *context)
+{
+    struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *) p->ip_payload;
+    SFIP_RET rc;
+    sfip_t *target_ip;
+    struct IPv6_Host *ip_entry;
+
+    target_ip = sfip_alloc_raw(&ns->nd_ns_target, AF_INET6, &rc);
+    if (rc != SFIP_SUCCESS) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "sfip_alloc_raw failed in %s:%d\n", __FILE__, __LINE__););
+        return;
+    };
+
+    // sfip_compare() is confusing, roll my own test for "::"
+    if (p->ip6h->ip_src.ip.u6_addr32[0]
+     || p->ip6h->ip_src.ip.u6_addr32[1]
+     || p->ip6h->ip_src.ip.u6_addr32[2]
+     || p->ip6h->ip_src.ip.u6_addr32[3]) {
+        /* src address set --> LLA resolution or reachability check */
+        return;
+    }
+
+    /* else:
+     * unspecified address
+     * --> i.e. new node tries to get address
+     * --> check if already known
+     */
+    ip_entry = get_host_entry(context->hosts, target_ip);
+    if (ip_entry) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Neighbour solicitation from known host\n"););
+        return;
+    }
+
+    /* this is the expected part: the IP is yet unknown --> put into DAD state */
+    ip_entry = create_dad_entry_ifnew(context->unconfirmed,
+                                       &p->pkt_header->ts,
+                                       p->ether_header->ether_source,
+                                       target_ip);
+    if (!ip_entry) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "create_dad_entry_ifnew failed in %s:%d\n", __FILE__, __LINE__););
+        return;
+    }
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "%s DAD started by %s / %s\n",
+                  pprint_ts(ip_entry->last_adv_ts),
+                  pprint_mac(ip_entry->ether_source),
+                  sfip_to_str(&ip_entry->ip)););
+    ALERT(SID_ICMP6_ND_NEW_DAD);
+}
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_constants.h snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_constants.h
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_constants.h	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_constants.h	2012-05-12 21:32:53.597269864 +0200
@@ -0,0 +1,190 @@
+/* 
+ * spp_ipv6_constants.h
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * Include file for
+ *  - network protocol constants and data structures not included in all OS
+ *  - own plugin-specific constants
+ *  - the plugin's SIDs
+ *
+ */
+
+#ifndef CONSTANTS_H
+#define	CONSTANTS_H
+
+#include "sf_snort_packet.h"
+#include <netinet/icmp6.h>
+
+/**********************************************************************
+ ** Protocol Constants                                               **
+ **********************************************************************/
+
+/* some constants, as already used in decode.c */
+#define IPPROTO_MOBILITY     135
+
+/* ICMPv6 types, http://www.iana.org/assignments/icmpv6-parameters */
+#define ICMP6_UNREACH 1
+#define ICMP6_BIG    2
+#define ICMP6_TIME   3
+#define ICMP6_PARAMS 4
+#define ICMP6_ECHO   128
+#define ICMP6_REPLY  129
+// NDP
+#define ICMP6_SOLICITATION    133
+#define ICMP6_ADVERTISEMENT   134
+#define ICMP6_N_SOLICITATION  135
+#define ICMP6_N_ADVERTISEMENT 136
+#define ICMP6_REDIRECT        137
+// Inverse ND
+#define ICMP6_INV_SOLICITATION    141
+#define ICMP6_INV_ADVERTISEMENT   142
+// Mobile IPv6
+#define ICMP6_HOME_AD_REQUEST  144
+#define ICMP6_HOME_AD_REPLY    145
+#define ICMP6_MOBILEPREFIX_SOL 146
+#define ICMP6_MOBILEPREFIX_ADV 147
+// SEND
+#define ICMP6_CRT_SOLICITATION  148
+#define ICMP6_CRT_ADVERTISEMENT 149
+// MIP6 Fast Handovers
+#define ICMP6_MOBILE_FH        154
+
+/* IPv6 ND Options, http://www.iana.org/assignments/icmpv6-parameters */
+#define ICMP6_OPT_HOMEAGENT  8
+#define ICMP6_OPT_CGA       11
+#define ICMP6_OPT_RSA       12
+#define ICMP6_OPT_TIMESTAMP 13
+#define ICMP6_OPT_NONCE     14
+#define ICMP6_OPT_ANCHOR    15
+#define ICMP6_OPT_CERT      16
+#define ICMP6_OPT_EXP1     253
+#define ICMP6_OPT_EXP2     254
+
+/* some more constants copied from BSD's <netinet/icmp6.h> */
+/* RFC2292 decls */
+#define ICMP6_MEMBERSHIP_QUERY          130     /* group membership query */
+#define ICMP6_MEMBERSHIP_REPORT         131     /* group membership report */
+#define ICMP6_MEMBERSHIP_REDUCTION      132     /* group membership termination */
+#define ICMP6_WRUREQUEST                139     /* who are you request */
+#define ICMP6_WRUREPLY                  140     /* who are you reply */
+#define ICMP6_FQDN_QUERY                139     /* FQDN query */
+#define ICMP6_FQDN_REPLY                140     /* FQDN reply */
+#define ICMP6_NI_QUERY                  139     /* node information request */
+#define ICMP6_NI_REPLY                  140     /* node information reply */
+#define MLDV2_LISTENER_REPORT           143     /* RFC3810 listener report */
+
+/* and some more constants copied from BSD's <netinet/in.h> */
+#define	IPPROTO_IPV4		4		/* IPv4 encapsulation */
+#define	IPPROTO_IPEIP		94		/* IP encapsulated in IP */
+#define	IPPROTO_ETHERIP		97		/* Ethernet IP encapsulation */
+
+/**********************************************************************
+ ** Protocol Data Formats                                            **
+ **********************************************************************/
+
+/*
+ * RA packet format
+ */
+struct ICMPv6_RA {
+    /* fixed part */
+    struct _ICMP6 icmp6h;
+    u_int8_t  nd_ra_cur_hop_limit;
+    union {
+        u_int8_t m:1,
+                 o:1,
+                 h:1,
+               prf:2,
+               res:3;
+        u_int8_t all;
+    } flags;
+    u_int16_t nd_ra_lifetime;
+    u_int32_t nd_ra_reachable;
+    u_int32_t nd_ra_retransmit;
+} __attribute__((packed));
+
+/* This array defines which ICMPv6 types may contain neighbor discovery options
+ * and contain their header lengths, i.e. the right offsets to find their options.
+ *
+ * (Most lengths are sizeof(struct nd_router_solicit) -- this is the basic ICMPv6
+ * type with 32 bits for type/code/checksum, 32 bits reserved or for identifiers,
+ * and possibly ND options starting in the 3nd 32-bit block.)
+ */
+inline static uint_fast8_t ND_hdrlen[255] = {
+    [ICMP6_SOLICITATION]      = sizeof(struct nd_router_solicit),
+    [ICMP6_ADVERTISEMENT]     = sizeof(struct nd_router_advert),
+    [ICMP6_N_SOLICITATION]    = sizeof(struct nd_neighbor_solicit),
+    [ICMP6_N_ADVERTISEMENT]   = sizeof(struct nd_neighbor_advert),
+    [ICMP6_REDIRECT]          = sizeof(struct nd_redirect),
+    [ICMP6_INV_SOLICITATION]  = sizeof(struct nd_router_solicit),
+    [ICMP6_INV_ADVERTISEMENT] = sizeof(struct nd_router_solicit),
+    [ICMP6_MOBILEPREFIX_ADV]  = sizeof(struct nd_router_solicit),
+    [ICMP6_CRT_SOLICITATION]  = sizeof(struct nd_router_solicit),
+    [ICMP6_CRT_ADVERTISEMENT] = sizeof(struct certpath_adv {struct icmp6_hdr hdr;
+                                                u_int16_t compact; u_int16_t reserved;}),
+    [ICMP6_MOBILE_FH]         = sizeof(struct nd_router_solicit),
+};
+
+/**********************************************************************
+ ** Snort-Plugin Constants                                           **
+ **********************************************************************/
+
+/*
+ * every preprocessor has a 32-bit ID.
+ * preprocids.h defines 20 standard IDs, we use magic numbers
+ */
+#define PP_IPv6   0xC0FFEE
+/* generator.h defines about 170 standard GIDs, we use a magic number */
+#define GEN_ID_IPv6 248
+
+// useful for memcpy calls
+#define MAC_LENGTH (6*sizeof(u_int8_t))
+
+/**********************************************************************
+ ** SIDs & descriptions for all alerts/warnings                      **
+ **********************************************************************/
+#define SID_ICMP6_RA_NEW_ROUTER             1
+#define SID_ICMP6_RA_NEW_ROUTER_TEXT        "ipv6: RA from new router"
+#define SID_ICMP6_INVALID_ROUTER_MAC        2
+#define SID_ICMP6_INVALID_ROUTER_MAC_TEXT   "ipv6: RA from non-router MAC address"
+#define SID_ICMP6_RA_PREFIX_CHANGED         3
+#define SID_ICMP6_RA_PREFIX_CHANGED_TEXT    "ipv6: RA prefix changed"
+#define SID_ICMP6_RA_FLAGS_CHANGED          4
+#define SID_ICMP6_RA_FLAGS_CHANGED_TEXT     "ipv6: RA flags changed"
+#define SID_ICMP6_RA_UNKNOWN_PREFIX         5
+#define SID_ICMP6_RA_UNKNOWN_PREFIX_TEXT    "ipv6: RA for non-local net prefix"
+#define SID_ICMP6_RA_LIFETIME0              6
+#define SID_ICMP6_RA_LIFETIME0_TEXT         "ipv6: RA with lifetime 0"
+#define SID_ICMP6_ND_NEW_DAD                7
+#define SID_ICMP6_ND_NEW_DAD_TEXT           "ipv6: new DAD started"
+#define SID_ICMP6_ND_NEW_HOST               8
+#define SID_ICMP6_ND_NEW_HOST_TEXT          "ipv6: new host in network"
+#define SID_ICMP6_INVALID_HOST_MAC          9
+#define SID_ICMP6_INVALID_HOST_MAC_TEXT     "ipv6: new host with non-allowed MAC address"
+#define SID_ICMP6_DAD_COLLISION             10
+#define SID_ICMP6_DAD_COLLISION_TEXT        "ipv6: DAD with collision"
+#define SID_ICMP6_DAD_DOS                   11
+#define SID_ICMP6_DAD_DOS_TEXT              "ipv6: DAD with spoofed collision"
+#define SID_ICMP6_LINKADDR_MISMATCH         12
+#define SID_ICMP6_LINKADDR_MISMATCH_TEXT    "ipv6: mismatch in MAC and NDP source linkaddress option"
+#define SID_IP6_ONLY_PADDING_EXT            13
+#define SID_IP6_ONLY_PADDING_EXT_TEXT       "ipv6: extension header has only padding options (evasion?)"
+#define SID_IP6_OPTION_LENGTH_ERR           14
+#define SID_IP6_OPTION_LENGTH_ERR_TEXT      "ipv6: option lengths != ext length"
+
+
+/* TODO:
+ * is it desirable to have only one alert per SID per packet?
+ * currently some SIDs test multiple attributes, thus can be raised more than once per packet.
+ */
+
+/* Macro for alerts */
+/* Arguments are: gid, sid, rev, classification, priority, message, rule_info */
+#define ALERT(x) { DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Raise Alert: %d, %s\n", x, x##_TEXT);); \
+                   _dpd.alertAdd(GEN_ID_IPv6, x, 1, 0, 3, x##_TEXT, 0 ); }
+/* different classification (?) 
+#define WARN(x)  { DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "Raise Warning: %d, %s\n", x, x##_TEXT);); \
+                   _dpd.alertAdd(GEN_ID_IPv6, x, 1, 1, 3, x##_TEXT, 0 ); }
+*/
+
+#endif	/* CONSTANTS_H */
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.c snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.c
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.c	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.c	2012-05-12 20:58:22.801272285 +0200
@@ -0,0 +1,520 @@
+/*
+ * spp_ipv6_data_structs.c
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * Insert/remove methods all data structures representing network state,
+ * especially lists and trees.
+ *
+ */
+
+#include "spp_ipv6_constants.h"
+#include "spp_ipv6_data_structs.h"
+
+
+/**********************************************************************
+ ** STAILQ IP_List                                                   **
+ **********************************************************************/
+
+/**
+ * Add IP (sfip_t) to a list.
+ * no input checking, arguments have to be valid.
+ */
+static void add_ip(struct IP_List_head *listhead, sfip_t *ip)
+{
+    struct IP_List *entry;
+    if (!(entry = (struct IP_List *) calloc(1, sizeof(struct IP_List))))
+        _dpd.fatalMsg("Could not allocate IPv6 dyn-pp configuration struct.\n");
+
+    entry->ip = ip;
+    STAILQ_INSERT_TAIL(listhead, entry, entries);
+}
+
+/**
+ * Check if IP matches a prefix in list.
+ */
+static bool ip_inprefixlist(struct IP_List_head *listhead, sfip_t *ip)
+{
+    struct IP_List *entry;
+
+    STAILQ_FOREACH(entry, listhead, entries) {
+        if (SFIP_EQUAL == sfip_contains(entry->ip, ip)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Check if IP is in list.
+ */
+static bool ip_inlist(struct IP_List_head *listhead, sfip_t *ip)
+{
+    struct IP_List *entry;
+
+    STAILQ_FOREACH(entry, listhead, entries) {
+        if (SFIP_EQUAL == sfip_compare(entry->ip, ip)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**********************************************************************
+ ** RB MAC_List / IPv6_Host                                          **
+ **********************************************************************/
+
+/**
+ * Compare MAC addesses
+ */
+static short mac_cmp(struct MAC_Entry *a, struct MAC_Entry *b)
+{
+    return memcmp(&a->mac, &b->mac, sizeof(a->mac));
+}
+
+/**
+ * Compare IPv6 hosts (only by IP)
+ *
+ * NB: no input checking; assume two valid IPv6 addresses
+ */
+static short host_cmp(struct IPv6_Host *a, struct IPv6_Host *b)
+{
+    // optimized
+    return memcmp(&a->ip.ip, &b->ip.ip, sizeof(b->ip.ip));
+
+    /* alternative with sfip api
+    SFIP_RET rc;
+    rc = sfip_compare(&a->ip, &b->ip);
+    switch (rc) {
+    case SFIP_LESSER:  return -1;
+    case SFIP_GREATER: return +1;
+    default:           return  0;
+    }
+    */
+}
+
+/**
+ * Parse a string MAC into binary data
+ * no input checking, arguments have to be valid
+ */
+static void mac_parse(const char* string, u_int8_t dst[])
+{
+    dst[0] = (u_int8_t) strtoul(&string[ 0], NULL, 16);
+    dst[1] = (u_int8_t) strtoul(&string[ 3], NULL, 16);
+    dst[2] = (u_int8_t) strtoul(&string[ 6], NULL, 16);
+    dst[3] = (u_int8_t) strtoul(&string[ 9], NULL, 16);
+    dst[4] = (u_int8_t) strtoul(&string[12], NULL, 16);
+    dst[5] = (u_int8_t) strtoul(&string[15], NULL, 16);
+}
+
+/**
+ * Aux. function to format MAC address (in static buffer).
+ */
+static char *pprint_mac(const u_int8_t ether_source[])
+{
+    static char buf[18];
+    snprintf(buf, sizeof(buf),
+             "%02x:%02x:%02x:%02x:%02x:%02x",
+             ether_source[0], ether_source[1],
+             ether_source[2], ether_source[3],
+             ether_source[4], ether_source[5]);
+    return buf;
+}
+
+/**
+ * Aux. function to format timestamp (in static buffer).
+ */
+static char *pprint_ts(const time_t ts)
+{
+    struct tm *printtm;
+    static char buf[64];
+
+    printtm = localtime(&ts);
+    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", printtm);
+
+    return buf;
+}
+
+
+/**
+ * Add string MAC to a tree.
+ * no input checking, arguments have to be valid.
+ *
+ * TODO: merge with normal add method
+ */
+static void mac_add(struct MAC_Entry_head *head, const char *mac)
+{
+    struct MAC_Entry *entry;
+
+    if (!(entry = (struct MAC_Entry *) calloc(1, sizeof(struct MAC_Entry))))
+        _dpd.fatalMsg("Could not allocate IPv6 dyn-pp configuration struct.\n");
+
+    mac_parse(mac, entry->mac);
+    RB_INSERT(MAC_Entry_data, &head->data, entry);
+}
+
+RB_GENERATE_STATIC(MAC_Entry_data,  MAC_Entry, entries, mac_cmp);
+RB_GENERATE_STATIC(IPv6_Hosts_data, IPv6_Host, entries, host_cmp);
+
+/**
+ * deletes an IPv6_Host entry
+ */
+static void del_host_entry(struct IPv6_Hosts_head *head,
+                           struct IPv6_Host *ip)
+{
+    if (RB_REMOVE(IPv6_Hosts_data, &head->data, ip)) {
+        sfip_free(ip->type.router.prefix);
+        free(ip);
+        head->entry_counter--;
+    } else {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "del_host_entry: RB_REMOVE failed\n"););
+    }
+}
+
+/**
+ * creates an IPv6_Host entry if it does not exist already (NULL on error)
+ * Note: only for DADs, i.e. the MAC layer uses 'ff:ff:ff:ff:ff:ff'
+ */
+static struct IPv6_Host *create_dad_entry_ifnew(struct IPv6_Hosts_head *head,
+                                                const struct timeval *tv,
+                                                const u_int8_t ether_source[],
+                                                const sfip_t *ip_src)
+{
+    return create_host_entry(head, tv, ether_source, ip_src);
+}
+
+/**
+ * deletes an IPv6_Host entry from DAD
+ */
+static void del_dad_entry(struct IPv6_Hosts_head *head,
+                          struct IPv6_Host *ip)
+{
+    del_host_entry(head, ip);
+}
+
+/**
+ * creates an IPv6_Host entry (or NULL on error)
+ */
+static struct IPv6_Host *create_host_entry(struct IPv6_Hosts_head *head,
+                                           const struct timeval *tv,
+                                           const u_int8_t ether_source[],
+                                           const sfip_t *ip_src)
+{
+    struct IPv6_Host *ip_entry = NULL;
+    struct IPv6_Host *ip_dupl  = NULL;
+
+    if (head->entry_limit && head->entry_limit <= head->entry_counter) {
+        _dpd.logMsg("MAC Tree @ 0x%x is full with %d elements,"
+                    " cannot add new entry\n",
+                    head, head->entry_counter);
+        return NULL;
+    }
+
+    if (!(ip_entry = (struct IPv6_Host *) calloc(1, sizeof (struct IPv6_Host)))) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "malloc failed in %s:%d\n", __FILE__, __LINE__););
+        return NULL;
+    }
+
+    // get IP (link-local)
+    if (sfip_set_ip(&ip_entry->ip, ip_src)) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "error in sfip_set_ip in %s:%d\n", __FILE__, __LINE__););
+        free(ip_entry);
+        return NULL;
+    }
+    // get MAC
+    memcpy(&ip_entry->ether_source, ether_source, MAC_LENGTH);
+    // get timestamp
+    ip_entry->last_adv_ts = tv->tv_sec;
+
+    // insert
+    if ((ip_dupl = RB_INSERT(IPv6_Hosts_data, &head->data, ip_entry))) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,
+                      "RB_INSERT failed in %s:%d\n\tnode %s/%s\n",
+                      __FILE__, __LINE__,
+                      pprint_mac(ip_dupl->ether_source),
+                      sfip_to_str(&ip_dupl->ip)););
+        return NULL;
+    } else {
+        head->entry_counter++;
+        //DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "RB_INSERT @ 0x%x, now %d entries\n", mac_head, mac_head->entry_counter););
+        return ip_entry;
+    }
+}
+
+/**
+ * use MAC list to retrieve on IPv6_Host entry (or NULL on error)
+ * i.e. search both layers
+ *
+ * FIXME: unused?
+ */
+static struct IPv6_Host *get_machost_entry(struct MAC_Entry_head *head,
+                                           const u_int8_t ether_source[],
+                                           const sfip_t *ip_src)
+{
+    struct MAC_Entry *mac = get_mac_entry(head, ether_source);
+
+    if (mac) return get_host_entry(mac->ips, ip_src);
+    else     return NULL;
+}
+
+/**
+ * retrieves an IPv6_Host entry (or NULL on error)
+ */
+static struct IPv6_Host *get_host_entry(struct IPv6_Hosts_head *head,
+                                        const sfip_t *ip_src)
+{
+    struct IPv6_Host ip_pivot;
+    if (sfip_set_ip(&ip_pivot.ip, ip_src)) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "error in sfip_set_ip in %s:%d\n", __FILE__, __LINE__););
+        return NULL;
+    }
+    return RB_FIND(IPv6_Hosts_data, &head->data, &ip_pivot);
+}
+
+/**
+ * retrieves a MAC_List entry (or NULL on error)
+ */
+static struct MAC_Entry *get_mac_entry(struct MAC_Entry_head *head,
+                                       const u_int8_t ether_source[])
+{
+    struct MAC_Entry mac_pivot = {
+        .mac = {ether_source[0], ether_source[1], ether_source[2],
+                ether_source[3], ether_source[4], ether_source[5]}
+    };
+    return RB_FIND(MAC_Entry_data, &head->data, &mac_pivot);
+}
+
+/**
+ * creates a MAC_List entry, including the IP-tree head
+ * returns entry, or NULL on error
+ */
+static struct MAC_Entry *create_mac_entry(struct MAC_Entry_head *head,
+                                          const u_int8_t ether_source[])
+{
+    struct MAC_Entry       *new_mac = NULL;
+    struct IPv6_Hosts_head *ip_head = NULL;
+
+    if (!(new_mac = (struct MAC_Entry *) calloc(1, sizeof (struct MAC_Entry)))) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "malloc failed in %s:%d\n", __FILE__, __LINE__););
+        return NULL;
+    }
+    if (!(ip_head = (struct IPv6_Hosts_head *) calloc(1, sizeof (struct IPv6_Hosts_head)))) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "malloc failed in %s:%d\n", __FILE__, __LINE__););
+        free(new_mac);
+        return NULL;
+    }
+
+    new_mac->ips = ip_head;
+    RB_INIT(&new_mac->ips->data);
+    memcpy(&new_mac->mac, ether_source, MAC_LENGTH);
+    if (RB_INSERT(MAC_Entry_data, &head->data, new_mac)) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "RB_INSERT failed in %s:%d\n", __FILE__, __LINE__););
+        free(new_mac);
+        free(ip_head);
+        return NULL;
+    } else {
+        return new_mac;
+    }
+}
+
+/**
+ * Add a router to a sorted list.
+ */
+static LISTOP_RET state_router_add(struct IPv6_Hosts_head *head,
+                                   struct IPv6_Host **elem,
+                                   const struct timeval* tv,
+                                   const u_int8_t ether_source[],
+                                   const sfip_t* ip_src,
+                                   sfip_t* prefix,
+                                   struct ICMPv6_RA* radv
+                                   )
+{
+    LISTOP_RET rc;
+    char old[64], new[64];
+
+    rc = state_host_add(head, elem, tv, ether_source, ip_src);
+
+    if (rc == LISTOP_ADDED) {
+        // new router: add prefix & flags
+        if (((*elem)->type.router.prefix = (sfip_t*) calloc(1, sizeof (sfip_t))))
+            (*elem)->type.router.prefix = prefix;
+        (*elem)->type.router.flags.all = radv->flags.all;
+        (*elem)->type.router.lifetime  = radv->nd_ra_lifetime;
+        return LISTOP_ADDED;
+    } else if (rc == LISTOP_UPDATED) {
+        // known router: verify prefix & flags
+
+        // TODO: check if one router may announce multiple prefixes
+        // the second cmp is a workaround because sfip_compare
+        if (sfip_compare((*elem)->type.router.prefix, prefix) != SFIP_EQUAL
+            || sfip_bits((*elem)->type.router.prefix) != sfip_bits(prefix)) {
+            snprintf(old, sizeof(old), "%s/%d",
+                     sfip_to_str((*elem)->type.router.prefix),
+                     sfip_bits((*elem)->type.router.prefix));
+            snprintf(new, sizeof(new), "%s/%d",
+                     sfip_to_str(prefix),
+                     sfip_bits(prefix));
+            DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "announced router prefix changed "
+                "from %s to %s\n", old, new););
+            ALERT(SID_ICMP6_RA_PREFIX_CHANGED);
+        }
+
+        if ((*elem)->type.router.flags.all != radv->flags.all ||
+            (*elem)->type.router.lifetime  != radv->nd_ra_lifetime
+        ) {
+            ALERT(SID_ICMP6_RA_FLAGS_CHANGED);
+            // keep it simple and just update everything after alert
+            (*elem)->type.router.flags.all = radv->flags.all;
+            (*elem)->type.router.lifetime  = radv->nd_ra_lifetime;
+        }
+    }
+    return rc;
+}
+
+/**
+ * add host to state
+ * ip_src is optional (if NULL, then package ip_src is used)
+ */
+static void confirm_host(const SFSnortPacket *p,
+                         struct IPv6_State *context,
+                         const sfip_t* ip_src)
+{
+    LISTOP_RET rc;
+    struct IPv6_Host *hostentry;
+    const sfip_t* target_ip = ip_src ? ip_src : &p->ip6h->ip_src;
+
+    rc = state_host_add(context->hosts,
+                        &hostentry,
+                        &p->pkt_header->ts,
+                        p->ether_header->ether_source,
+                        target_ip);
+    if (rc == LISTOP_ADDED) {
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "%s new IPv6 host: %s / %s\n",
+                      pprint_ts(p->pkt_header->ts.tv_sec),
+                      pprint_mac(p->ether_header->ether_source),
+                      sfip_to_str(target_ip)););
+        if (context->config->host_whitelist
+            && !RB_EMPTY(&context->config->host_whitelist->data)
+            && !get_mac_entry(context->config->host_whitelist, p->ether_header->ether_source)) {
+            ALERT(SID_ICMP6_INVALID_HOST_MAC);
+        } else {
+            ALERT(SID_ICMP6_ND_NEW_HOST);
+        }
+    } else if (rc == LISTOP_UPDATED) { // nothing
+    } else { // (rc == LISTOP_ERROR)
+        DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "IPv6 PP: cannot add state for IPv6 host %s / %s\n",
+                      pprint_mac(p->ether_header->ether_source),
+                      sfip_to_str(target_ip)););
+    }
+}
+
+/**
+ * Add a host to a sorted MAC/IP list. If entry exists then only update timestamp.
+ *
+ * Returns address of added host entry in *elem.
+ */
+static LISTOP_RET state_host_add(struct IPv6_Hosts_head *head,
+                                 struct IPv6_Host **elem,
+                                 const struct timeval* tv,
+                                 const u_int8_t ether_source[],
+                                 const sfip_t* ip_src)
+{
+
+    /* MAC entry exists --> check if IP is already known */
+    struct IPv6_Host *ip_entry = get_host_entry(head, ip_src);
+
+    if (ip_entry) { // IP exists --> update timestamp and exit
+        ip_entry->last_adv_ts = tv->tv_sec;
+        *elem = ip_entry;
+        return LISTOP_UPDATED;
+    }
+
+    /* else: either MAC existed without IP, or MAC was just created *
+     * --> anyway, now create IPv6_Host                             */
+    *elem = create_host_entry(head, tv, ether_source, ip_src);
+    if (!*elem)
+        return LISTOP_ERROR;
+    else
+        return LISTOP_ADDED;
+}
+
+/**
+ * Auxillary function to print uniform lists of hosts and routers.
+ */
+static void state_host_printlist(struct IPv6_Hosts_head *head)
+{
+    struct IPv6_Host *host;
+    char routerinfo[128];
+
+    RB_FOREACH(host, IPv6_Hosts_data, &head->data) {
+        if (host->type.router.prefix && sfip_is_set(host->type.router.prefix)) {
+        // for routers:
+            snprintf(routerinfo, sizeof (routerinfo),
+                     "\n\t-- prefix %s/%d, lifetime %d sec, flags %s%s%s, pref %s",
+                     sfip_to_str(host->type.router.prefix),
+                     sfip_bits(host->type.router.prefix),
+                     host->type.router.lifetime,
+                     host->type.router.flags.m ? "M" : "-",
+                     host->type.router.flags.o ? "O" : "-",
+                     host->type.router.flags.h ? "H" : "-",
+                     (host->type.router.flags.prf == 0 ? "default" :
+                         (host->type.router.flags.prf == 3 ? "low" :
+                             (host->type.router.flags.prf == 1 ? "high" : "reserved")))
+            );
+        } else
+            routerinfo[0] = '\0';
+
+        _dpd.logMsg("MAC %s -- IP %s -- last seen: %s%s\n",
+                    pprint_mac(host->ether_source),
+                    sfip_to_str(&host->ip),
+                    pprint_ts(host->last_adv_ts),
+                    routerinfo);
+    }
+}
+
+/**
+ * Auxillary function to expire entries from state.
+ * now is the current timestamp,
+ * keep indicates the hold time, should come from config->keep_state
+ *
+ * returns number of entries after deletions
+ */
+static u_int32_t state_host_expirelist(struct IPv6_Hosts_head *head, time_t now, time_t keep)
+{
+    struct IPv6_Host *var, *nxt;
+    u_int32_t entries = 0;
+
+    for (var = RB_MIN(IPv6_Hosts_data, &head->data); var != NULL; var = nxt) {
+        nxt = RB_NEXT(IPv6_Hosts_data, &head->data, var);
+
+        if (now - var->last_adv_ts > keep) { // expired
+            DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN, "%s expire %s / %s after %d secs (leaves %d entries)\n",
+                          pprint_ts(now),
+                          pprint_mac(var->ether_source),
+                          sfip_to_str(&var->ip),
+                          now - var->last_adv_ts,
+                          head->entry_counter - 1););
+            del_host_entry(head, var);
+        } else {
+            entries++;
+        }
+    }
+    return entries;
+}
+
+/**
+ * Aux. function to get memory consumtion
+ */
+static size_t state_host_memusage(struct IPv6_Hosts_head *head)
+{
+    struct IPv6_Host *var;
+    size_t size = 0;
+
+    size += sizeof(*head);
+    RB_FOREACH(var, IPv6_Hosts_data, &head->data) {
+        size += sizeof(*var);
+        if (var->type.router.prefix)
+            size += sizeof(*var->type.router.prefix);
+    }
+    return size;
+}
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.h snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.h
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.h	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_data_structs.h	2012-05-12 20:58:22.837603821 +0200
@@ -0,0 +1,215 @@
+/*
+ * spp_ipv6_data_structs.h
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * Include file for all data structures representing network state,
+ * especially lists and trees with prototypes of their respective
+ * insert/remove methods.
+ * 
+ */
+
+#ifndef DATASTRUCTS_H
+#define	DATASTRUCTS_H
+#include "spp_ipv6.h"
+
+/**********************************************************************
+ ** Structures/Data Types                                            **
+ **********************************************************************/
+
+/* return values for list operations */
+typedef enum _list_op_return_values {
+    LISTOP_ADDED = 0, // entry added
+    LISTOP_UPDATED,   // entry updatet (e.g. new timestamp)
+    LISTOP_EXISTS,    // entry already exists, no change
+    LISTOP_ERROR      // error, e.g. malloc() failed
+} LISTOP_RET;
+
+/*
+ * State for this module, i.e. seen hosts and adresses.
+ * (current size: 60 bytes on i386, 88 bytes on amd64)
+ */
+
+// second level is a tree of IPs (per MAC)
+struct IPv6_Host {
+    // common data for all hosts
+    RB_ENTRY(IPv6_Host) entries;
+    u_int8_t ether_source[6];
+    time_t last_adv_ts;
+    sfip_t ip;
+    // extra data for routers/DAD detection
+    union {
+        struct {
+            sfip_t *prefix;
+            u_int16_t lifetime;
+            union {
+                u_int8_t m:1,
+                         o:1,
+                         h:1,
+                       prf:2;
+                u_int8_t all;
+            } flags;
+        } router;
+        struct {
+            sfip_t *noprefix;    // FIXME: currently only non-NULL in this field indicates a router entry
+            u_int32_t contacted; // count if any 2nd host sends request
+        } dad;
+    } type;
+};
+RB_HEAD(IPv6_Hosts_data, IPv6_Host);
+
+struct IPv6_Hosts_head {
+    struct IPv6_Hosts_data data;
+    u_int32_t entry_limit;
+    u_int32_t entry_counter;
+};
+
+/*
+ * this struct has two uses:
+ *   - with ips = NULL it holds a list of MAC addresses for configuration options
+ *   - with ips set it is the first level of state information
+ */
+struct MAC_Entry {
+    RB_ENTRY(MAC_Entry) entries;
+    u_int8_t mac[6];
+    struct IPv6_Hosts_head *ips;
+} __attribute__((aligned));
+RB_HEAD(MAC_Entry_data, MAC_Entry);
+
+struct MAC_Entry_head {
+    struct MAC_Entry_data data;
+    u_int32_t entry_limit;
+    u_int32_t entry_counter;
+};
+
+// List because it has few entries and may use prefixes instead of IPs
+struct IP_List {
+    STAILQ_ENTRY(IP_List) entries;
+    sfip_t *ip;
+};
+STAILQ_HEAD(IP_List_head, IP_List);
+
+/*
+ * Note on the data structures used:
+ *  o configuration settings
+ *    - router_whitelist, host_whitelist
+ *      each is one rb-tree of MAC_List entries
+ *    - prefix_whitelist
+ *      is one stailq of IP_List
+ *  o network state
+ *    - routers, hosts, unconfirmed/dads
+ *      each one is an rb-tree of IPv6_Host entries
+ *
+ */
+
+/**********************************************************************
+ ** Function Prototypes                                              **
+ **********************************************************************/
+static void confirm_host(
+    const SFSnortPacket *p,
+    struct IPv6_State *context,
+    const sfip_t* ip_src
+) __attribute__((nonnull(1, 2)));
+static LISTOP_RET state_host_add(
+    struct IPv6_Hosts_head*,
+    struct IPv6_Host**,
+    const struct timeval*,
+    const u_int8_t[],
+    const sfip_t*
+) __attribute__((nonnull(1, 3, 4, 5)));
+static LISTOP_RET state_router_add(
+    struct IPv6_Hosts_head*,
+    struct IPv6_Host **,
+    const struct timeval*,
+    const u_int8_t[],
+    const sfip_t*,
+    sfip_t*,
+    struct ICMPv6_RA*
+) __attribute__((nonnull(1, 3, 4, 5, 6)));
+static u_int32_t state_host_expirelist(
+    struct IPv6_Hosts_head*,
+    time_t,
+    time_t
+) __attribute__((nonnull));
+static size_t state_host_memusage(
+    struct IPv6_Hosts_head*
+) __attribute__((nonnull));
+static void state_host_printlist(
+    struct IPv6_Hosts_head *
+) __attribute__((nonnull));
+static char *pprint_mac(
+    const u_int8_t[]
+) __attribute__((nonnull));
+static char *pprint_ts(
+    const time_t ts
+);
+static void mac_parse(
+    const char* string,
+    u_int8_t dst[]
+)__attribute__((nonnull));
+static void mac_add(
+    struct MAC_Entry_head*,
+    const char*
+)__attribute__((nonnull));
+static short mac_cmp(
+    struct MAC_Entry*,
+    struct MAC_Entry*
+)__attribute__((nonnull));
+static short host_cmp(
+    struct IPv6_Host*,
+    struct IPv6_Host*
+)__attribute__((nonnull));
+static void add_ip(
+    struct IP_List_head*,
+    sfip_t*
+)__attribute__((nonnull));
+static bool ip_inprefixlist(
+    struct IP_List_head*,
+    sfip_t*
+)__attribute__((nonnull,unused));
+static bool ip_inlist(
+    struct IP_List_head*,
+    sfip_t*
+)__attribute__((nonnull));
+static struct IPv6_Host *get_machost_entry(
+    struct MAC_Entry_head*,
+    const u_int8_t[],
+    const sfip_t*
+)__attribute__((nonnull,unused));
+static struct MAC_Entry *get_mac_entry(
+    struct MAC_Entry_head*,
+    const u_int8_t[]
+)__attribute__((nonnull));
+static struct MAC_Entry *create_mac_entry(
+    struct MAC_Entry_head*,
+    const u_int8_t[]
+)__attribute__((malloc,nonnull,unused));
+static struct IPv6_Host *get_host_entry(
+    struct IPv6_Hosts_head*,
+    const sfip_t*
+)__attribute__((nonnull));
+static struct IPv6_Host *create_host_entry(
+    struct IPv6_Hosts_head*,
+    const struct timeval*,
+    const u_int8_t[],
+    const sfip_t*
+)__attribute__((malloc,nonnull));
+static struct IPv6_Host *create_dad_entry_ifnew(
+    struct IPv6_Hosts_head*,
+    const struct timeval*,
+    const u_int8_t[],
+    const sfip_t*
+)__attribute__((malloc,nonnull)); //create_host_entry_ifnew
+static void del_host_entry(
+    struct IPv6_Hosts_head*,
+    struct IPv6_Host*
+)__attribute__((nonnull));
+static void del_dad_entry(
+    struct IPv6_Hosts_head*,
+    struct IPv6_Host*
+)__attribute__((nonnull));
+
+RB_PROTOTYPE_STATIC(MAC_Entry_data,  MAC_Entry, entries, mac_cmp);
+RB_PROTOTYPE_STATIC(IPv6_Hosts_data, IPv6_Host, entries, host_cmp);
+
+#endif	/* DATASTRUCTS_H */
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6.h snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6.h
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6.h	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6.h	2012-05-12 21:29:34.089267983 +0200
@@ -0,0 +1,191 @@
+/*
+ * spp_ipv6.h
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _SPP_IPV6_H
+#define	_SPP_IPV6_H
+
+/**********************************************************************
+ ** Includes                                                         **
+ **********************************************************************/
+#include "sf_types.h"
+#include <time.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#ifdef __linux__
+#ifndef __unused
+#define __unused __attribute__((__unused__)) 
+#endif /* __unused */
+#include "tree.h"
+#else /* BSD */
+#include <sys/tree.h>
+#endif /* __linux__  */
+
+#include "preprocids.h"
+#include "sf_snort_packet.h"
+#include "sf_dynamic_preproc_lib.h"
+#include "sf_dynamic_preprocessor.h"
+#include "snort_debug.h"
+#include "sfPolicy.h"
+#include "sfPolicyUserData.h"
+/* for ICMPv6 format */
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <netinet/in.h>
+
+/* verify string contains a MAC address */
+#define IS_MAC(string) ((string) != NULL                                     \
+  && isxdigit((string)[ 0]) && isxdigit((string)[ 1]) && (string)[ 2] == ':' \
+  && isxdigit((string)[ 3]) && isxdigit((string)[ 4]) && (string)[ 5] == ':' \
+  && isxdigit((string)[ 6]) && isxdigit((string)[ 7]) && (string)[ 8] == ':' \
+  && isxdigit((string)[ 9]) && isxdigit((string)[10]) && (string)[11] == ':' \
+  && isxdigit((string)[12]) && isxdigit((string)[13]) && (string)[14] == ':' \
+  && isxdigit((string)[15]) && isxdigit((string)[16]) && (string)[17] == '\0')
+
+
+/**********************************************************************
+ ** Structures/Data Types                                            **
+ **********************************************************************/
+
+/*
+ * Some simple statistics.
+ * TODO: only for data exploration; to be removed later on
+ */
+struct IPv6_Statistics {
+    uint32_t pkt_seen;
+    uint32_t pkt_invalid;
+    uint32_t pkt_icmpv6;
+    uint32_t pkt_other;
+
+    uint32_t pkt_fragments;
+
+    uint32_t pkt_ip6h;
+
+    uint32_t pkt_icmp_rsol;
+    uint32_t pkt_icmp_radv;
+    uint32_t pkt_icmp_nsol;
+    uint32_t pkt_icmp_nadv;
+
+    uint32_t pkt_icmp_mlquery;
+    uint32_t pkt_icmp_mlreport;
+    uint32_t pkt_icmp_unreach;
+    uint32_t pkt_icmp_other;
+};
+
+/*
+ * configuration and plugin state.
+ */
+struct IPv6_Config {
+    u_int32_t keep_state_duration;  // in sec
+    u_int32_t expire_run_interval;  // in sec
+    u_int32_t max_routers;
+    u_int32_t max_hosts;
+    u_int32_t max_unconfirmed;
+    bool      track_ndp;
+    bool      report_prefix_change;
+    bool      report_new_routers;
+    bool      report_new_hosts;
+    struct MAC_Entry_head *router_whitelist;
+    struct MAC_Entry_head *host_whitelist;
+    struct IP_List_head   *prefix_whitelist;
+} __attribute__((packed));
+
+struct IPv6_State {
+    struct IPv6_Hosts_head *routers;   // known routers
+    struct IPv6_Hosts_head *hosts;     // established hosts
+    struct IPv6_Hosts_head *unconfirmed;  // ongoing duplicate detections/solicitations
+    struct IPv6_Statistics *stat;
+    struct IPv6_Config     *config;
+    time_t next_expire;
+} __attribute__((packed));
+
+/*
+ * for Rule Options
+ */
+enum IPv6_RuleOpt_Type {
+    IPV6_RULETYPE_IPV,
+    IPV6_RULETYPE_IP6EXTHDR,
+    IPV6_RULETYPE_IP6EXTCOUNT,
+    IPV6_RULETYPE_FLOWLABEL,
+    IPV6_RULETYPE_TRAFFICCLASS,
+    IPV6_RULETYPE_OPTION,
+    IPV6_RULETYPE_OPTION_EXT,
+    IPV6_RULETYPE_OPTVAL,
+    IPV6_RULETYPE_ND,
+    IPV6_RULETYPE_ND_OPTION,
+    IPV6_RULETYPE_RH,
+    IPV6_RULETYPE_EXT_ORDERED
+};
+
+enum cmp_op {
+    check_eq=0, check_neq,
+    check_lt, check_gt,
+    check_and, check_xor, check_nand
+};
+
+struct IPv6_RuleOpt_Data {
+#ifdef DEBUG
+    char *debugname;
+    char *debugparam;
+#endif /* DEBUG */
+    enum IPv6_RuleOpt_Type type:4;
+    enum cmp_op op:4;
+    union {
+            u_int32_t number;
+            struct { // for ip6_optval
+                u_int8_t  ext_type;
+                u_int8_t  opt_type;
+                u_int16_t opt_value;
+            } exthdr;
+    } opt;
+} __attribute__((packed));
+
+/**********************************************************************
+ ** Function Prototypes                                              **
+ **********************************************************************/
+
+static void IPv6_Init(char *);
+static void IPv6_Process(void *, void *);
+static void IPv6_Process_ICMPv6(const SFSnortPacket *, struct IPv6_State *);
+static void IPv6_Process_ICMPv6_RA(const SFSnortPacket *, struct IPv6_State *);
+static void IPv6_Process_ICMPv6_RA_stateless(const SFSnortPacket *);
+static void IPv6_Process_ICMPv6_NA(const SFSnortPacket *, struct IPv6_State *);
+static void IPv6_Process_ICMPv6_NS(const SFSnortPacket *, struct IPv6_State *);
+inline static void IPv6_UpdateStats(const SFSnortPacket *, struct IPv6_Statistics *);
+inline static void IPv6_Process_ND_Options(const SFSnortPacket *, struct IPv6_State *);
+inline static void IPv6_Process_Extensions(const SFSnortPacket *, struct IPv6_State *);
+static void IPv6_PrintStats(int);
+static void IPv6_ResetStats(int, void *);
+static void IPv6_Parse(char *, struct IPv6_Config *);
+
+static int IPv6_Rule_Init(char *, char *, void **);
+static int IPv6_Rule_Eval(void *, const u_int8_t **, void *);
+static u_int32_t IPv6_Rule_Hash(void *);
+static int IPv6_Rule_KeyCompare(void *, void *);
+
+static void set_default_config(struct IPv6_Config *config);
+inline static void read_num(char **arg, const char *param, u_int32_t *configptr);
+
+#endif	/* _SPP_IPV6_H */
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_parse.c snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_parse.c
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_parse.c	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_parse.c	2012-05-12 21:28:49.153268906 +0200
@@ -0,0 +1,124 @@
+/*
+ * spp_ipv6_parse.c
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Description:
+ *  Function to parse configuration options in snort.conf
+ *
+ */
+
+#include "spp_ipv6.h"
+#include <errno.h>
+
+/**
+ * Parse the configuration options in snort.conf
+ *
+ * Currently supported options: router_mac, host_mac, net_prefix
+ */
+void set_default_config(struct IPv6_Config *config)
+{
+    config->track_ndp = true;
+    // for testing: 1h, later: 2-12h
+    config->keep_state_duration = 60*60;
+    config->expire_run_interval = 20*60;
+    // not sure if these are realistic, should be high enough
+    config->max_routers     = 32;
+    config->max_hosts       = 8192;
+    config->max_unconfirmed = 32768;
+
+    return;
+}
+
+#define BIN_OPTION(X, Y) if (!strcasecmp(X, arg)) {         \
+                             (Y) = false;                   \
+                             _dpd.logMsg("  " X "\n");      \
+                             arg = strtok(NULL, " \t\n\r"); \
+                         }
+
+inline void read_num(char **arg, const char *param, u_int32_t *configptr)
+{
+    uint_fast32_t minutes;
+    *arg = strtok(NULL, " \t\n\r");
+    minutes = (uint_fast32_t) strtoul(*arg, NULL, 10);
+    if (errno) {
+        _dpd.fatalMsg("  Invalid parameter to %s\n", param);
+    }
+    *configptr = 60 * minutes;
+    _dpd.logMsg("  %s = %u minutes = %u secs\n",
+                param, minutes, *configptr);
+    *arg = strtok(NULL, " \t\n\r");
+}
+
+void IPv6_Parse(char *args, struct IPv6_Config *config)
+{
+    char *arg;
+    char ismac;
+    sfip_t *prefix;
+    SFIP_RET rc;
+
+    set_default_config(config);
+    _dpd.logMsg("IPv6 preprocessor config:\n");
+    if (!args) {
+        _dpd.logMsg("\tno additional parameters\n");
+        return;
+    }
+
+    arg = strtok(args, " \t\n\r");
+    while (arg) {
+        if(!strcasecmp("router_mac", arg)) { // and now a list of 0-n router MACs
+            config->report_new_routers = true;
+            while ((arg = strtok(NULL, ", \t\n\r")) && (ismac = IS_MAC(arg))) {
+                mac_add(config->router_whitelist, arg);
+                _dpd.logMsg("  default router MAC %s\n", arg);
+            }
+        } else if(!strcasecmp("host_mac", arg)) { // and now a list of 0-n host MACs
+            config->report_new_hosts = true;
+            while ((arg = strtok(NULL, ", \t\n\r")) && (ismac = IS_MAC(arg))) {
+                mac_add(config->host_whitelist, arg);
+                _dpd.logMsg("  default host MAC %s\n", arg);
+            }
+        } else if(!strcasecmp("net_prefix", arg)) { // and now a list of 0-n prefixes
+            config->report_prefix_change = true;
+            while ((arg = strtok(NULL, ", \t\n\r")) && strchr(arg, '/')) {  // TODO remove /-check
+                prefix = sfip_alloc(arg, &rc);
+                if (rc == SFIP_SUCCESS) {
+                    add_ip(config->prefix_whitelist, prefix);
+                    _dpd.logMsg("  default net prefix %s/%d\n",
+                        sfip_to_str(prefix), sfip_bits(prefix));
+                } else {
+                    _dpd.fatalMsg("  Invalid prefix %s\n", arg);
+                }
+            }
+        } else if(!strcasecmp("max_routers", arg)) {
+            read_num(&arg, "max_routers", &(config->max_routers));
+        } else if(!strcasecmp("max_hosts", arg)) {
+            read_num(&arg, "max_hosts", &(config->max_hosts));
+        } else if(!strcasecmp("max_unconfirmed", arg)) {
+            read_num(&arg, "max_unconfirmed", &(config->max_unconfirmed));
+        } else if(!strcasecmp("keep_state", arg)) {
+            read_num(&arg, "keep_state", &(config->keep_state_duration));
+        } else if(!strcasecmp("expire_run", arg)) {
+            read_num(&arg, "expire_run", &(config->expire_run_interval));
+        } else BIN_OPTION("disable_tracking", config->track_ndp)
+          else {
+            _dpd.fatalMsg("IPv6: Invalid option %s\n", arg);
+        }
+    }
+}
+
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_ruleopt.c snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_ruleopt.c
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/spp_ipv6_ruleopt.c	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/spp_ipv6_ruleopt.c	2012-05-12 20:58:22.817266777 +0200
@@ -0,0 +1,551 @@
+/*
+ * spp_ipv6_ruleopt.c
+ *
+ * Copyright (C) 2011 Martin Schuette <info@mschuette.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.  You may not use, modify or
+ * distribute this program under any other version of the GNU General
+ * Public License.
+ *
+ * This program 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Description:
+ *   IPv6 Preprocessor functions related to rule option handling.
+ *
+ */
+
+#include <errno.h>
+#include "spp_ipv6.h"
+#include "spp_ipv6_constants.h"
+#include "spp_ipv6_data_structs.h"
+// for rule option:
+#include "sf_snort_plugin_api.h"
+// for IPv6_Rule_Hash
+#include "sfhashfcn.h"
+
+/* reads whitespace and optional comparison operator;
+ * advances the char* and returns the operator to use
+ */
+static enum cmp_op get_op(char **param_ptr)
+{
+    enum cmp_op op = check_eq;
+    if (!*param_ptr) return op; // if called w/o params
+
+    while (isblank(**param_ptr)) (*param_ptr)++;
+    switch(*param_ptr[0]) {
+    case '=': op = check_eq;  (*param_ptr)++; break;
+    case '!': op = check_neq; (*param_ptr)++; break;
+    case '<': op = check_lt;  (*param_ptr)++; break;
+    case '>': op = check_gt;  (*param_ptr)++; break;
+    case '&': op = check_and; (*param_ptr)++; break;
+    case '^': op = check_xor; (*param_ptr)++; break;
+    case '|': op = check_nand;(*param_ptr)++; break;
+    }
+    while (isblank(**param_ptr)) (*param_ptr)++;
+    return op;
+}
+
+/* reads a decimal or hexadecimal number.
+ * FatalMessage on error
+ */
+static u_int32_t get_num(char **param_ptr, const char *name)
+{
+    char *endptr;
+    u_int32_t parameter;
+    if (*param_ptr) {
+        while (isblank(**param_ptr)) (*param_ptr)++;
+        if (isdigit(*param_ptr[0])) {
+            if ((*param_ptr)[0] == '0' && (*param_ptr)[1] == 'x') {
+                (*param_ptr)++; (*param_ptr)++;
+                parameter = (u_int32_t) _dpd.SnortStrtoul(*param_ptr, &endptr, 16);
+            } else
+                parameter = (u_int32_t) _dpd.SnortStrtoul(*param_ptr, &endptr, 10);
+
+            if (!errno && endptr && (*endptr == '\0'))
+                return parameter;
+        }
+    }
+    DynamicPreprocessorFatalMessage("%s(%d) => keyword %s with invalid number %s\n",
+        *(_dpd.config_file), *(_dpd.config_line), name, (*param_ptr ? *param_ptr : ""));
+}
+
+/* copied from src/detection-plugins/sf_snort_plugin_hdropts.c */
+inline static bool checkField(enum cmp_op op, uint_fast32_t value1, uint_fast32_t value2)
+{
+    switch (op)
+    {
+        case check_eq:
+            if (value1 == value2)
+                return true;
+            break;
+        case check_neq:
+            if (value1 != value2)
+                return true;
+            break;
+        case check_lt:
+            if (value1 < value2)
+                return true;
+            break;
+        case check_gt:
+            if (value1 > value2)
+                return true;
+            break;
+        case check_and:
+            if (value1 & value2)
+                return true;
+            break;
+        case check_xor:
+            if (value1 ^ value2)
+                return true;
+            break;
+        case check_nand:
+            if (!(value1 & value2))
+                return true;
+            break;
+    }
+
+    return false;
+}
+
+/* Check order of extension headers */
+static bool IPv6_Check_Ext_Order(SFSnortPacket *p)
+{
+    u_int8_t order[] = {IPPROTO_HOPOPTS, IPPROTO_ROUTING, IPPROTO_FRAGMENT,
+            IPPROTO_AH, IPPROTO_ESP, IPPROTO_DSTOPTS, IPPROTO_MOBILITY};
+    u_short size = sizeof(order)/sizeof(u_int8_t);
+    u_short i = 0, c = 0;
+
+    while (i < p->num_ip6_extensions) {
+        if (c >= size)
+            return false; // i.e. option_type not found, not good
+        else if (order[c] == IPPROTO_ROUTING
+             && p->ip6_extensions[i].option_type == IPPROTO_DSTOPTS
+             && p->ip6_extensions[i+1].option_type == IPPROTO_ROUTING) {
+            // special case: a 2nd dst opt hdr may be in front of a routing hdr,
+            c++; i++; i++;
+        } else if (p->ip6_extensions[i].option_type == order[c]) { // option_type good
+            c++; i++;
+        } else { // order[c] not present, check next
+            c++;
+        }
+    }
+    return true;
+}
+
+/* Parsing for rule options */
+static int IPv6_Rule_Init(char *name, char *params, void **data)
+{
+    struct IPv6_RuleOpt_Data *sdata;
+    enum IPv6_RuleOpt_Type ruletype;
+#ifdef DEBUG
+    char *orig_params = params ? strdup(params) : "" ; // to preserve full option in **data
+    char *orig_name = strdup(name);    // to preserve full name   in **data
+#endif /* DEBUG */
+
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES, "IPv6_rule_init(\"%s\", \"%s\", ...)\n",
+        orig_name, orig_params););
+
+    if (!strcmp(name, "ipv"))
+        ruletype = IPV6_RULETYPE_IPV;
+    else if (!strcmp(name, "ip6_tclass"))
+        ruletype = IPV6_RULETYPE_TRAFFICCLASS;
+    else if (!strcmp(name, "ip6_flow"))
+        ruletype = IPV6_RULETYPE_FLOWLABEL;
+    else if (!strcmp(name, "ip6_exthdr"))
+        ruletype = IPV6_RULETYPE_IP6EXTHDR;
+    else if (!strcmp(name, "ip6_extnum"))
+        ruletype = IPV6_RULETYPE_IP6EXTCOUNT;
+    else if (!strcmp(name, "ip6_option"))
+        ruletype = IPV6_RULETYPE_OPTION;
+    else if (!strcmp(name, "ip6_optval"))
+        ruletype = IPV6_RULETYPE_OPTVAL;
+    else if (!strcmp(name, "ip6_rh"))
+        ruletype = IPV6_RULETYPE_RH;
+    else if (!strcmp(name, "ip6_ext_ordered"))
+        ruletype = IPV6_RULETYPE_EXT_ORDERED;
+    else if (!strcmp(name, "icmp6_nd"))
+        ruletype = IPV6_RULETYPE_ND;
+    else if (!strcmp(name, "icmp6_nd_option"))
+        ruletype = IPV6_RULETYPE_ND_OPTION;
+    else
+        DynamicPreprocessorFatalMessage("%s(%d) => invalid keyword %s\n",
+            *(_dpd.config_file), *(_dpd.config_line), name);
+
+    sdata = (struct IPv6_RuleOpt_Data *)calloc(1, sizeof(*sdata));
+    if (sdata == NULL)
+        DynamicPreprocessorFatalMessage("Could not allocate memory for the "
+                "%s preprocessor rule option.\n", name);
+    sdata->type       = ruletype;
+#ifdef DEBUG
+    sdata->debugparam = strdup(orig_params);
+    sdata->debugname  = strdup(orig_name);
+#endif /* DEBUG */
+
+    if (!params
+    && ruletype != IPV6_RULETYPE_ND
+    && ruletype != IPV6_RULETYPE_EXT_ORDERED) {
+            DynamicPreprocessorFatalMessage("%s(%d) => keyword %s requires parameter\n",
+                *(_dpd.config_file), *(_dpd.config_line), name);
+    }
+
+    switch(ruletype) {
+    case IPV6_RULETYPE_EXT_ORDERED:     /* fallthrough */
+    case IPV6_RULETYPE_ND:
+        sdata->op = get_op(&params);
+        // no numeric params, just ignore them if given
+        break;
+    case IPV6_RULETYPE_IPV:             /* fallthrough */
+    case IPV6_RULETYPE_IP6EXTCOUNT:     /* fallthrough */
+    case IPV6_RULETYPE_IP6EXTHDR:       /* fallthrough */
+    case IPV6_RULETYPE_FLOWLABEL:       /* fallthrough */
+    case IPV6_RULETYPE_TRAFFICCLASS:    /* fallthrough */
+    case IPV6_RULETYPE_ND_OPTION:       /* fallthrough */
+    case IPV6_RULETYPE_RH:
+        sdata->op         = get_op(&params);
+        sdata->opt.number = get_num(&params, name);
+        break;
+    case IPV6_RULETYPE_OPTION: {
+        /* NB: the ip6_optval/IPV6_RULETYPE_OPTVAL was
+         * split off to clarify the user documentation.
+         *
+         * Internally the three syntax variants are mapped
+         * to three ruletypes, but the functional difference
+         * is small and they share most code in IPv6_Rule_Eval().
+         */
+        /* syntax:
+         * <op>Y   --> normal, test options for <op>Y
+         * <op>X.Y --> test for <op>Y in ext./nd. type X
+         */
+        char *split;
+        sdata->op = get_op(&params);                           // <op>
+        split = strchr(params, '.');
+        if (!split) {
+            sdata->opt.exthdr.ext_type = 0;
+            sdata->opt.exthdr.opt_type = get_num(&params, name);   // Y
+        } else {
+            sdata->type = IPV6_RULETYPE_OPTION_EXT;
+            *split++ = '\0';
+            sdata->opt.exthdr.ext_type = get_num(&params, name);   // X
+            sdata->opt.exthdr.opt_type = get_num(&split, name);    // Y
+        }
+        break;
+    }
+    case IPV6_RULETYPE_OPTVAL: {
+        /* syntax: X.Y<op>Z --> test for value Z<op>Y in ext. type X */
+        char *split;
+        char *op, *orig_op;
+        u_int32_t tmp;
+
+        split = strchr(params, '.');
+        *split++ = '\0';
+        op = strpbrk(split, " =!<>^&");
+
+        sdata->opt.exthdr.ext_type = get_num(&params, name);   // X
+        // have to remember the position for \0:
+        orig_op = op;
+        sdata->op = get_op(&op);                               // <op>
+        *orig_op = '\0';
+        sdata->opt.exthdr.opt_type = get_num(&split, name);    // Y
+        
+        // handle possibly larger Z values:
+        tmp = get_num(&op, name);
+        if (tmp > UINT16_MAX) {
+            free(sdata);
+            DynamicPreprocessorFatalMessage(
+                "%s(%d) => keyword %s only supports 16-bit values\n",
+                *(_dpd.config_file), *(_dpd.config_line), name, params);
+        }
+        sdata->opt.exthdr.opt_value = (u_int16_t) tmp;         // Z
+        break;
+    }
+    default:
+        free(sdata);
+        DynamicPreprocessorFatalMessage("%s(%d) => invalid ruletype %s\n",
+            *(_dpd.config_file), *(_dpd.config_line), name);
+    }
+
+    /* verify given modifiers */
+    switch(ruletype) {
+    case IPV6_RULETYPE_IP6EXTHDR:       /* fallthrough */
+    case IPV6_RULETYPE_EXT_ORDERED:     /* fallthrough */
+    case IPV6_RULETYPE_RH:              /* fallthrough */
+    case IPV6_RULETYPE_ND:
+        if (sdata->op != check_eq && sdata->op != check_neq)
+            DynamicPreprocessorFatalMessage("%s(%d) => keyword %s "
+                "only allows the operators '=' or '!'\n",
+                *(_dpd.config_file), *(_dpd.config_line), name);
+        break;
+    case IPV6_RULETYPE_IP6EXTCOUNT:
+        if (sdata->op != check_eq
+         && sdata->op != check_neq
+         && sdata->op != check_gt
+         && sdata->op != check_lt)
+            DynamicPreprocessorFatalMessage("%s(%d) => keyword %s "
+                "only allows the operators '=', '!', '<', or '>'\n",
+                *(_dpd.config_file), *(_dpd.config_line), name);
+        break;
+    default: // all modifiers OK
+        break;
+    }
+
+    *data = (void *)sdata;
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES, " --> IPv6_rule_init returns data 0x%x\n", *data););
+    return 1;
+}
+
+static u_int32_t IPv6_Rule_Hash(void *d)
+{
+    u_int32_t a,b,c;
+    struct IPv6_RuleOpt_Data *sdata = (struct IPv6_RuleOpt_Data *)d;
+
+    a = sdata->type;
+    b = sdata->opt.number;
+    c = sdata->op & PP_IPv6;
+
+    final(a,b,c);
+    return c;
+}
+
+static int IPv6_Rule_KeyCompare(void *l, void *r)
+{
+    struct IPv6_RuleOpt_Data *left = (struct IPv6_RuleOpt_Data *)l;
+    struct IPv6_RuleOpt_Data *right = (struct IPv6_RuleOpt_Data *)r;
+
+    if (left && right
+        && left->type == right->type
+        && left->op   == right->op
+        && left->opt.number == right->opt.number) {
+        DEBUG_WRAP(DebugMessage(DEBUG_RULES, "IPv6_Rule_KeyCompare(%x, %x) --> equal\n", l, r););
+        return PREPROC_OPT_EQUAL;
+    }
+
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES, "IPv6_Rule_KeyCompare(%x, %x) --> not equal\n", l, r););
+    return PREPROC_OPT_NOT_EQUAL;
+}
+
+#define RETURN_MATCH do {                                                \
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES,                                \
+        "IPv6_rule_eval on raw_packet at 0x%x\n\twith option %s: %s "    \
+        "(internally: option %u, op %u, value 0x%04x)\n\tmatches\n",        \
+        raw_packet, sdata->debugname, sdata->debugparam,                 \
+        sdata->type, sdata->op, sdata->opt.number););                    \
+    return RULE_MATCH;                                                   \
+} while (0)
+#define RETURN_NOMATCH do {                                              \
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES,                                \
+        "IPv6_rule_eval on raw_packet at 0x%x\n\twith option %s: %s "    \
+        "(internally: option %u, op %u, value 0x%04x)\n\tdoes not match\n", \
+        raw_packet, sdata->debugname, sdata->debugparam,                 \
+        sdata->type, sdata->op, sdata->opt.number););                    \
+    return RULE_NOMATCH;                                                 \
+} while (0)
+
+
+/* Rule option evaluation */
+static int IPv6_Rule_Eval(void *raw_packet, const u_int8_t **cursor, void *data)
+{
+    SFSnortPacket *p = (SFSnortPacket*) raw_packet;
+    struct IPv6_RuleOpt_Data *sdata = (struct IPv6_RuleOpt_Data *) data;
+    DEBUG_WRAP(DebugMessage(DEBUG_RULES, "IPv6_Rule_Eval()\n"););
+
+    if (!p || !sdata) {
+        _dpd.errMsg("Error in IPv6_Rule_Eval(): missing packet or option data\n");
+        return RULE_NOMATCH;
+    }
+    // small optimization: except ipv all rules match IPv6 only
+    if (!p->ip6h && (sdata->type != IPV6_RULETYPE_IPV)) {
+        return RULE_NOMATCH;
+    }
+
+    switch (sdata->type) {
+    case IPV6_RULETYPE_IPV: {
+        uint_fast8_t ipv = GET_IPH_VER(p);
+        if (ipv != 6 && ipv != 4) {
+            _dpd.errMsg("buggy Snort version: ip6_ret_ver() uses wrong byte order\n");
+            ipv = ntohl(p->ip6h->vcl) >> 28;
+        }
+
+        if (checkField(sdata->op, ipv, sdata->opt.number))
+            RETURN_MATCH;
+        else
+            RETURN_NOMATCH;
+    }
+    case IPV6_RULETYPE_IP6EXTCOUNT:
+        if (checkField(sdata->op, p->num_ip6_extensions, sdata->opt.number))
+            RETURN_MATCH;
+        else
+            RETURN_NOMATCH;
+    case IPV6_RULETYPE_FLOWLABEL:
+        if (checkField(sdata->op, (ntohl(p->ip6h->vcl) & 0x000fffff), sdata->opt.number))
+            RETURN_MATCH;
+        else
+            RETURN_NOMATCH;
+    case IPV6_RULETYPE_TRAFFICCLASS: {
+        uint_fast8_t tos;
+        tos = (ntohl(p->ip6h->vcl) & 0x0ff00000) >> 20;
+        if (checkField(sdata->op, tos, sdata->opt.number))
+            RETURN_MATCH;
+        else
+            RETURN_NOMATCH;
+    }
+    case IPV6_RULETYPE_EXT_ORDERED: {
+        bool rc = IPv6_Check_Ext_Order(p);
+        if ((rc && sdata->op == check_eq)
+        || (!rc && sdata->op == check_neq))
+            RETURN_MATCH;
+        else
+            RETURN_NOMATCH;
+    }
+    case IPV6_RULETYPE_IP6EXTHDR: {
+        uint_fast8_t i;
+
+        for (i = 0; i < p->num_ip6_extensions; i++)
+            if (p->ip6_extensions[i].option_type == sdata->opt.number) {
+                if (sdata->op == check_eq)  RETURN_MATCH;
+                if (sdata->op == check_neq) RETURN_NOMATCH;
+            }
+        if (sdata->op == check_neq) RETURN_MATCH;
+        break;
+    }
+    case IPV6_RULETYPE_RH: {
+        uint_fast8_t i;
+
+        for (i = 0; i < p->num_ip6_extensions; i++)
+            if (p->ip6_extensions[i].option_type == IPPROTO_ROUTING) {
+                struct ip6_rthdr *rt_hdr = (struct ip6_rthdr *)
+                    p->ip6_extensions[i].option_data;
+                if (rt_hdr->ip6r_type == sdata->opt.number) {
+                    if (sdata->op == check_eq)  RETURN_MATCH;
+                    if (sdata->op == check_neq) RETURN_NOMATCH;
+                }
+            }
+        if (sdata->op == check_neq) RETURN_MATCH;
+        break;
+    }
+    case IPV6_RULETYPE_ND:
+        if (!p->icmp_header) {
+            // no ICMPv6 or a type that may not contain rule options
+            if (sdata->op == check_neq) RETURN_MATCH;
+            else                        RETURN_NOMATCH;
+        }
+        switch (p->icmp_header->type) {
+        case ICMP6_SOLICITATION:      /* fallthrough */
+        case ICMP6_ADVERTISEMENT:     /* fallthrough */
+        case ICMP6_N_SOLICITATION:    /* fallthrough */
+        case ICMP6_N_ADVERTISEMENT:   /* fallthrough */
+        case ICMP6_REDIRECT:          /* fallthrough */
+        case ICMP6_INV_SOLICITATION:  /* fallthrough */
+        case ICMP6_INV_ADVERTISEMENT: /* fallthrough */
+        case ICMP6_HOME_AD_REQUEST:   /* fallthrough */
+        case ICMP6_HOME_AD_REPLY:     /* fallthrough */
+        case ICMP6_MOBILEPREFIX_SOL:  /* fallthrough */
+        case ICMP6_MOBILEPREFIX_ADV:  /* fallthrough */
+        case ICMP6_CRT_SOLICITATION:  /* fallthrough */
+        case ICMP6_CRT_ADVERTISEMENT: /* fallthrough */
+        case ICMP6_MOBILE_FH:
+            if (sdata->op == check_eq)
+                RETURN_MATCH;
+            else
+                RETURN_NOMATCH;
+        default:
+            if (sdata->op == check_neq)
+                RETURN_MATCH;
+            else
+                RETURN_NOMATCH;
+        }
+    case IPV6_RULETYPE_ND_OPTION: {
+        if (!p->icmp_header || !ND_hdrlen[p->icmp_header->type]) {
+            // no ICMPv6 or a type that may not contain rule options
+            if (sdata->op == check_neq) RETURN_MATCH;
+            else                        RETURN_NOMATCH;
+        } else {
+            uint_fast8_t icmp_hdr_len = ND_hdrlen[p->icmp_header->type];
+            uint_fast16_t len  = p->ip_payload_size - icmp_hdr_len;
+            struct nd_opt_hdr *option = (struct nd_opt_hdr *) (p->ip_payload + icmp_hdr_len);
+            while (len) {
+                uint_fast8_t optlen = 8 * (option->nd_opt_len);
+                bool rc = checkField(sdata->op, option->nd_opt_type, sdata->opt.number);
+                if ( rc && sdata->op != check_neq) RETURN_MATCH;
+                if (!rc && sdata->op == check_neq) RETURN_NOMATCH;
+                if (optlen > len) {
+                    _dpd.logMsg("IPv6 decoder problem. malformed ND option lenghts.\n");
+                    break;
+                }
+                len -= optlen;
+                option = (struct nd_opt_hdr *) ((char *)option + optlen);
+            }
+            if (sdata->op == check_neq) RETURN_MATCH;
+            else                        RETURN_NOMATCH;
+        }
+    }
+    case IPV6_RULETYPE_OPTVAL:     /* fallthrough */
+    case IPV6_RULETYPE_OPTION:     /* fallthrough */
+    case IPV6_RULETYPE_OPTION_EXT: {
+        uint_fast8_t i;
+
+        for (i = 0; i < p->num_ip6_extensions; i++) {
+            if (p->ip6_extensions[i].option_type != IPPROTO_HOPOPTS
+                && p->ip6_extensions[i].option_type != IPPROTO_DSTOPTS)
+                continue;
+            if ((sdata->type == IPV6_RULETYPE_OPTION_EXT
+                || sdata->type == IPV6_RULETYPE_OPTVAL)
+                && p->ip6_extensions[i].option_type != sdata->opt.exthdr.ext_type)
+                continue;
+
+            {
+            /* hbh_hdr   is the Ext Hdr with type and length
+             * hbh_hdr+1 is the first option with type and length
+             * cursor    iterates over the options
+             */
+            struct ip6_hbh *hbh_hdr = (struct ip6_hbh*) p->ip6_extensions[i].option_data;
+            u_int16_t ext_len = (hbh_hdr->ip6h_len + 1) << 3;
+            u_int8_t *cursor  = (u_int8_t *) (hbh_hdr+1);
+            u_int8_t *ext_end = ((u_int8_t *) hbh_hdr) + ext_len;
+
+            while (cursor < ext_end) {
+                struct ip6_opt *opt = (struct ip6_opt*) cursor;
+
+                DEBUG_WRAP(DebugMessage(DEBUG_RULES,
+                        "checkOpt(type %u, len %u at 0x%04x)\n",
+                        opt->ip6o_type, opt->ip6o_len, opt););
+
+                // NB: ext_type already checked in outer loop
+                if (sdata->type == IPV6_RULETYPE_OPTION
+                 || sdata->type == IPV6_RULETYPE_OPTION_EXT) {
+                    bool rc = checkField(sdata->op, opt->ip6o_type, sdata->opt.exthdr.opt_type);
+                    DEBUG_WRAP(DebugMessage(DEBUG_RULES,
+                        "checkField(%u, 0x%04x, 0x%04x) = %s\n",
+                        sdata->op, opt->ip6o_type, sdata->opt.exthdr.opt_type,
+                        (rc ? "match" : "fail")););
+                    if ( rc && sdata->op != check_neq) RETURN_MATCH;
+                    if (!rc && sdata->op == check_neq) RETURN_NOMATCH;
+                } else if (sdata->type == IPV6_RULETYPE_OPTVAL
+                    && opt->ip6o_type == sdata->opt.exthdr.opt_type) {
+                        u_int16_t *ptr = (u_int16_t*) opt;
+                        u_int16_t val = ntohs(*(ptr+1));
+                        bool rc = checkField(sdata->op, val, sdata->opt.exthdr.opt_value);
+                        if ( rc && sdata->op != check_neq) RETURN_MATCH;
+                        if (!rc && sdata->op == check_neq) RETURN_NOMATCH;
+                }
+
+                cursor += (opt->ip6o_type == 0) ? 1    // Pad1 is special
+                    : (2+opt->ip6o_len);
+            }
+            }
+        }
+        if (sdata->op == check_neq) RETURN_MATCH;
+        else                        RETURN_NOMATCH;
+        break;
+    }
+    }
+    RETURN_NOMATCH;
+}
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/tree.h snort-2.9.2.2/src/dynamic-preprocessors/ipv6/tree.h
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/ipv6/tree.h	1970-01-01 01:00:00.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/ipv6/tree.h	2012-05-12 20:58:22.861276272 +0200
@@ -0,0 +1,765 @@
+/*	$NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $	*/
+/*	$OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $	*/
+/* $FreeBSD: src/sys/sys/tree.h,v 1.9.2.1.4.1 2010/06/14 02:09:06 kensmith Exp $ */
+
+/*-
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	_SYS_TREE_H_
+#define	_SYS_TREE_H_
+
+#include <sys/cdefs.h>
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *	- every search path from the root to a leaf consists of the
+ *	  same number of black nodes,
+ *	- each red node (except for the root) has a black parent,
+ *	- each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define SPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define SPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define SPLAY_ROOT(head)		(head)->sph_root
+#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (/*CONSTCOND*/ 0)
+	
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {				\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
+	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_SPLAY(struct name *, struct type *);			\
+void name##_SPLAY_MINMAX(struct name *, int);				\
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static __inline struct type *						\
+name##_SPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (SPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_SPLAY(head, elm);					\
+	if (SPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = SPLAY_RIGHT(elm, field);				\
+		while (SPLAY_LEFT(elm, field) != NULL) {		\
+			elm = SPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static __inline struct type *						\
+name##_SPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_SPLAY_MINMAX(head, val);					\
+        return (SPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (SPLAY_EMPTY(head)) {						\
+	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_SPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (SPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+			name##_SPLAY(head, elm);			\
+			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_SPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {		\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}									\
+									\
+/* Splay with either the minimum or the maximum element			\
+ * Used to find minimum or maximum element in tree.			\
+ */									\
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define SPLAY_NEGINF	-1
+#define SPLAY_INF	1
+
+#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)					\
+	for ((x) = SPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define RB_INITIALIZER(root)						\
+	{ NULL }
+
+#define RB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK	0
+#define RB_RED		1
+#define RB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+
+#define RB_LEFT(elm, field)		(elm)->field.rbe_left
+#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define RB_COLOR(elm, field)		(elm)->field.rbe_color
+#define RB_ROOT(head)			(head)->rbh_root
+#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {					\
+	RB_PARENT(elm, field) = parent;					\
+	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
+	RB_COLOR(elm, field) = RB_RED;					\
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do {				\
+	RB_COLOR(black, field) = RB_BLACK;				\
+	RB_COLOR(red, field) = RB_RED;					\
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)	do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = RB_RIGHT(elm, field);					\
+	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {	\
+		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {	\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_LEFT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = RB_LEFT(elm, field);					\
+	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {	\
+		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {	\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_RIGHT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define	RB_PROTOTYPE(name, type, field, cmp)				\
+	RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define	RB_PROTOTYPE_STATIC(name, type, field, cmp)			\
+	RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)		\
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *);		\
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *);	\
+attr struct type *name##_RB_INSERT(struct name *, struct type *);	\
+attr struct type *name##_RB_FIND(struct name *, struct type *);		\
+attr struct type *name##_RB_NFIND(struct name *, struct type *);	\
+attr struct type *name##_RB_NEXT(struct type *);			\
+attr struct type *name##_RB_PREV(struct type *);			\
+attr struct type *name##_RB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define	RB_GENERATE(name, type, field, cmp)				\
+	RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define	RB_GENERATE_STATIC(name, type, field, cmp)			\
+	RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)		\
+attr void								\
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = RB_PARENT(elm, field)) != NULL &&		\
+	    RB_COLOR(parent, field) == RB_RED) {			\
+		gparent = RB_PARENT(parent, field);			\
+		if (parent == RB_LEFT(gparent, field)) {		\
+			tmp = RB_RIGHT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_RIGHT(parent, field) == elm) {		\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = RB_LEFT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_LEFT(parent, field) == elm) {		\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
+}									\
+									\
+attr void								\
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
+	    elm != RB_ROOT(head)) {					\
+		if (RB_LEFT(parent, field) == elm) {			\
+			tmp = RB_RIGHT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = RB_RIGHT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_RIGHT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = RB_LEFT(tmp, field)) \
+					    != NULL)			\
+						RB_COLOR(oleft, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = RB_RIGHT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_RIGHT(tmp, field))		\
+					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = RB_LEFT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = RB_LEFT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_LEFT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = RB_RIGHT(tmp, field)) \
+					    != NULL)			\
+						RB_COLOR(oright, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = RB_LEFT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_LEFT(tmp, field))		\
+					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		RB_COLOR(elm, field) = RB_BLACK;			\
+}									\
+									\
+attr struct type *							\
+name##_RB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (RB_LEFT(elm, field) == NULL)				\
+		child = RB_RIGHT(elm, field);				\
+	else if (RB_RIGHT(elm, field) == NULL)				\
+		child = RB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = RB_RIGHT(elm, field);				\
+		while ((left = RB_LEFT(elm, field)) != NULL)		\
+			elm = left;					\
+		child = RB_RIGHT(elm, field);				\
+		parent = RB_PARENT(elm, field);				\
+		color = RB_COLOR(elm, field);				\
+		if (child)						\
+			RB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (RB_LEFT(parent, field) == elm)		\
+				RB_LEFT(parent, field) = child;		\
+			else						\
+				RB_RIGHT(parent, field) = child;	\
+			RB_AUGMENT(parent);				\
+		} else							\
+			RB_ROOT(head) = child;				\
+		if (RB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (RB_PARENT(old, field)) {				\
+			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+				RB_LEFT(RB_PARENT(old, field), field) = elm;\
+			else						\
+				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+			RB_AUGMENT(RB_PARENT(old, field));		\
+		} else							\
+			RB_ROOT(head) = elm;				\
+		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
+		if (RB_RIGHT(old, field))				\
+			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				RB_AUGMENT(left);			\
+			} while ((left = RB_PARENT(left, field)) != NULL); \
+		}							\
+		goto color;						\
+	}								\
+	parent = RB_PARENT(elm, field);					\
+	color = RB_COLOR(elm, field);					\
+	if (child)							\
+		RB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (RB_LEFT(parent, field) == elm)			\
+			RB_LEFT(parent, field) = child;			\
+		else							\
+			RB_RIGHT(parent, field) = child;		\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = child;					\
+color:									\
+	if (color == RB_BLACK)						\
+		name##_RB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+attr struct type *							\
+name##_RB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = RB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	RB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			RB_LEFT(parent, field) = elm;			\
+		else							\
+			RB_RIGHT(parent, field) = elm;			\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = elm;					\
+	name##_RB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+attr struct type *							\
+name##_RB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+/* Finds the first node greater than or equal to the search key */	\
+attr struct type *							\
+name##_RB_NFIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *res = NULL;					\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0) {						\
+			res = tmp;					\
+			tmp = RB_LEFT(tmp, field);			\
+		}							\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (res);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_RB_NEXT(struct type *elm)					\
+{									\
+	if (RB_RIGHT(elm, field)) {					\
+		elm = RB_RIGHT(elm, field);				\
+		while (RB_LEFT(elm, field))				\
+			elm = RB_LEFT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_RB_PREV(struct type *elm)					\
+{									\
+	if (RB_LEFT(elm, field)) {					\
+		elm = RB_LEFT(elm, field);				\
+		while (RB_RIGHT(elm, field))				\
+			elm = RB_RIGHT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+attr struct type *							\
+name##_RB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else							\
+			tmp = RB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define RB_NEGINF	-1
+#define RB_INF	1
+
+#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y)	name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
+#define RB_PREV(name, x, y)	name##_RB_PREV(y)
+#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)					\
+	for ((x) = RB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y)					\
+	for ((x) = (y);							\
+	    ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y)				\
+	for ((x) = RB_MIN(name, head);					\
+	    ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head)				\
+	for ((x) = RB_MAX(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y)				\
+	for ((x) = (y);							\
+	    ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)			\
+	for ((x) = RB_MAX(name, head);					\
+	    ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);	\
+	     (x) = (y))
+
+#endif	/* _SYS_TREE_H_ */
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/Makefile.am snort-2.9.2.2/src/dynamic-preprocessors/Makefile.am
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/Makefile.am	2011-10-26 20:28:52.000000000 +0200
+++ snort-2.9.2.2/src/dynamic-preprocessors/Makefile.am	2012-05-12 20:59:38.837269339 +0200
@@ -488,7 +488,7 @@
 endif
 
 if HAVE_DYNAMIC_PLUGINS
-SUBDIRS = . libs ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf sip reputation gtp modbus dnp3 $(RZB_SAAC_DIR)
+SUBDIRS = . libs ipv6 ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf sip reputation gtp modbus dnp3 $(RZB_SAAC_DIR)
 endif
 
 clean-local:
diff -uNr snort-2.9.2.2-clean/src/dynamic-preprocessors/Makefile.in snort-2.9.2.2/src/dynamic-preprocessors/Makefile.in
--- snort-2.9.2.2-clean/src/dynamic-preprocessors/Makefile.in	2012-03-19 23:55:55.000000000 +0100
+++ snort-2.9.2.2/src/dynamic-preprocessors/Makefile.in	2012-05-12 20:59:46.917267380 +0200
@@ -127,7 +127,7 @@
 	distdir
 ETAGS = etags
 CTAGS = ctags
-DIST_SUBDIRS = . libs ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf \
+DIST_SUBDIRS = . libs ipv6 ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf \
 	sip reputation gtp modbus dnp3 rzb_saac
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
