diff --git a/preproc_rules/decoder.rules b/preproc_rules/decoder.rules
index b3dd84d..186aec0 100644
--- a/preproc_rules/decoder.rules
+++ b/preproc_rules/decoder.rules
@@ -143,3 +143,59 @@ alert ( msg:"DECODE_IP6_EXCESS_EXT_HDR"; sid:456; gid:116; rev:1; metadata:rule-
 alert ( msg:"DECODE_ICMPV6_UNREACHABLE_NON_RFC_4443_CODE"; sid:457; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
 alert ( msg:"DECODE_IPV6_BAD_FRAG_PKT"; sid:458; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
 alert ( msg:"DECODE_ZERO_LENGTH_FRAG"; sid:459; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_DGRAM_LT_SCTPHDR"; sid:320; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CSUM_IS_ADLER32"; sid:321; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CHUNK_ZERO_LENGTH"; sid:322; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CHUNK_LEN_GT_DGRAM"; sid:323; gid:116; rev:1; metadata:rule-type decode; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_DATA_C_BAD_LEN"; sid:324; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_DATA_C_RSVD_NZERO"; sid:325; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_BAD_LEN"; sid:326; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_FLAGS_NZERO"; sid:327; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_ITAG_ZERO"; sid:328; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_VTAG_NZERO"; sid:329; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_NOUTSTR_ZERO"; sid:330; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_C_NINSTR_ZERO"; sid:331; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_BAD_LEN"; sid:332; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO"; sid:333; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_ITAG_ZERO"; sid:334; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO"; sid:335; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO"; sid:336; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SACK_C_BAD_LEN"; sid:337; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.4; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SACK_C_FLAGS_NZERO"; sid:338; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.4; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_C_BAD_LEN"; sid:339; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.5; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO"; sid:340; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.5; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN"; sid:341; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.6; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO"; sid:342; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.6; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ABORT_C_BAD_LEN"; sid:343; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.7; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ABORT_C_RSVD_NZERO"; sid:344; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.7; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_C_BAD_LEN"; sid:345; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.8; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO"; sid:346; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.8; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN"; sid:347; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.9; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO"; sid:348; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.9; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ERROR_C_BAD_LEN"; sid:349; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.10; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ERROR_C_FLAGS_NZERO"; sid:350; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.10; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN"; sid:351; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.11; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO"; sid:352; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.11; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ACK_C_BAD_LEN"; sid:353; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.12; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO"; sid:354; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.12; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ECNE_C_BAD_LEN"; sid:355; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ECNE_C_FLAGS_NZERO"; sid:356; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CWR_C_BAD_LEN"; sid:357; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_CWR_C_FLAGS_NZERO"; sid:358; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#appendix-A; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN"; sid:359; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.13; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO"; sid:360; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4960#section-3.3.13; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_AUTH_C_BAD_LEN"; sid:361; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4895#section-5.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_AUTH_C_FLAGS_NZERO"; sid:362; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4895#section-5.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_ACK_C_BAD_LEN"; sid:363; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO"; sid:364; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_BAD_LEN"; sid:365; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-12#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_FLAGS_NZERO"; sid:366; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-12#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PKTDROP_C_RSVD_NZERO"; sid:367; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-stewart-sctp-pktdrprep-12#section-4.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_BAD_LEN"; sid:368; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-ietf-tsvwg-sctp-strrst-12#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_RECONFIG_C_FLAGS_NZERO"; sid:369; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/draft-ietf-tsvwg-sctp-strrst-12#section-3.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PAD_C_BAD_LEN"; sid:370; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4820#section-3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_PAD_C_FLAGS_NZERO"; sid:371; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc4820#section-3; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_FORWARD_TSN_C_BAD_LEN"; sid:372; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc3758#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO"; sid:373; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc3758#section-3.2; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_C_BAD_LEN"; sid:374; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.1; classtype:protocol-command-decode; )
+alert ( msg:"DECODE_SCTP_ASCONF_C_FLAGS_NZERO"; sid:375; gid:116; rev:1; metadata:rule-type decode; reference:url,tools.ietf.org/html/rfc5061#section-4.1.1; classtype:protocol-command-decode; )
diff --git a/preproc_rules/preprocessor.rules b/preproc_rules/preprocessor.rules
index 6258919..d85bb9e 100644
--- a/preproc_rules/preprocessor.rules
+++ b/preproc_rules/preprocessor.rules
@@ -81,6 +81,14 @@ alert ( msg: "PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN"; sid: 24; gid: 122; rev: 1
 alert ( msg: "PSNG_ICMP_PORTSWEEP"; sid: 25; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "PSNG_ICMP_PORTSWEEP_FILTERED"; sid: 26; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "PSNG_OPEN_PORT"; sid: 27; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSCAN"; sid: 28; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_DECOY_PORTSCAN"; sid: 29; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSWEEP"; sid: 30; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_DISTRIBUTED_PORTSCAN"; sid: 31; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_PORTSCAN"; sid: 32; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_DECOY_PORTSCAN"; sid: 33; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_PORTSWEEP_FILTERED"; sid: 34; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
+alert ( msg: "PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN"; sid: 35; gid: 122; rev: 1; metadata: rule-type preproc ; classtype:attempted-recon; )
 alert ( msg: "FRAG3_IPOPTIONS"; sid: 1; gid: 123; rev: 1; metadata: rule-type preproc ; classtype:protocol-command-decode; )
 alert ( msg: "FRAG3_TEARDROP"; sid: 2; gid: 123; rev: 1; metadata: rule-type preproc ; reference:cve,1999-0015; reference:bugtraq,124; classtype:attempted-dos; )
 alert ( msg: "FRAG3_SHORT_FRAG"; sid: 3; gid: 123; rev: 1; metadata: rule-type preproc ; classtype:protocol-command-decode; )
diff --git a/src/decode.c b/src/decode.c
index 2b3679a..762c93a 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -57,6 +57,8 @@
 #include "mempool.h"
 #include "spp_normalize.h"
 #include "sfdaq.h"
+#include "sfutil/adler32.h"
+#include "sfutil/crc32.h"
 
 extern tSfActionQueueId decoderActionQ;
 extern MemPool decoderAlertMemPool;
@@ -272,6 +274,16 @@ static inline void execIcmpChksmDrop (void *data)
     }
 }
 
+static inline void execSctpChksmDrop (void *data)
+{
+    if( ScInlineMode() && ScSctpChecksumDrops() )
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+            "Dropping bad packet (SCTP checksum)\n"););
+        Active_DropPacket();
+    }
+}
+
 void execDecoderEvent(void *data)
 {
     MemBucket *alertBucket = (MemBucket *)data;
@@ -2547,7 +2559,13 @@ void DecodeIP(const uint8_t * pkt, const uint32_t len, Packet * p)
                 //ClearDumpBuf();
                 return;
 
-#ifdef GRE
+            case IPPROTO_SCTP:
+                pc.sctp++;
+                DecodeSCTP(pkt + hlen, ip_len, p);
+                //ClearDumpBuf();
+                return;
+
+ #ifdef GRE
             case IPPROTO_IPV6:
                 if (ip_len < 40)
                 {
@@ -2946,6 +2964,15 @@ void DecodeICMPEmbeddedIP(const uint8_t *pkt, const uint32_t len, Packet *p)
 
             break;
 
+        case IPPROTO_SCTP:
+            p->orig_sctph = (SCTPHdr *)(pkt + hlen);
+
+            /* stuff more data into the printout data struct */
+            p->orig_sp = ntohs(p->orig_sctph->sh_sport);
+            p->orig_dp = ntohs(p->orig_sctph->sh_dport);
+
+            break;
+
         case IPPROTO_ICMP:
             p->orig_icmph = (ICMPHdr *)(pkt + hlen);
             break;
@@ -2955,6 +2982,679 @@ void DecodeICMPEmbeddedIP(const uint8_t *pkt, const uint32_t len, Packet *p)
 }
 
 /*
+ * Function: SctpCrc32Chksum(const unsigned char* buf, unsigned int len)
+ *
+ * Purpose: Calculate the SCTP checksum using CRC32 (per RFC3309).
+ *
+ * Arguments: buf => ptr to the SCTP packet
+ *            len => length from here to the end of the packet
+ *
+ * Returns: uint32_t checksum value (CRC32)
+ */
+uint32_t SctpCrc32Chksum(const unsigned char* buf, unsigned int len)
+{
+  uint16_t i;
+  uint32_t crc32 = CRC32C_PRELOAD, result;
+
+  /* Checksum the common header EXCEPT the checksum field */
+  for (i = 0; i < (SCTP_COMMON_HEADER_LEN - 4); i++)
+      CRC32C(crc32, buf[i]);
+
+  /* Checksum four '0' bytes as the checksum value */
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+  CRC32C(crc32, 0);
+
+  /* Checksum the remainder of the packet */
+  for (i = SCTP_COMMON_HEADER_LEN; i < len; i++)
+      CRC32C(crc32, buf[i]);
+
+  result = CRC32C_SWAP(crc32);
+
+  return (~result);
+}
+
+/*
+ * Function: SctpAdler32Chksum(const unsigned char* buf, unsigned int len)
+ *
+ * Purpose: Calculate the SCTP checksum using Adler32 (per RFC2960).
+ *
+ * Arguments: buf => ptr to the SCTP packet
+ *            len => length from here to the end of the packet
+ *
+ * Returns: uint32_t checksum value (Adler32)
+ */
+static unsigned int
+SctpAdler32Chksum(const unsigned char* buf, unsigned int len)
+{
+  uint32_t result = 1L;
+
+  /* Checksum four '0' bytes as the checksum value */
+  result = update_adler32(result, buf, (SCTP_COMMON_HEADER_LEN - 4));
+
+  /* handle four 0 bytes as checksum */
+  result = update_adler32(result, (unsigned char *)"\0\0\0\0", 4);
+
+  /* Checksum the remainder of the packet */
+  result = update_adler32(result, (buf + SCTP_COMMON_HEADER_LEN),
+                          (len - SCTP_COMMON_HEADER_LEN));
+
+  return result;
+}
+
+/* SCTP-layer decoder alerts */
+static inline void SctpChunkAnomaly(Packet *p, const uint16_t length,
+    const char *debug_msg, const uint16_t de_code, char *de_str)
+{
+    DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "%s!\n", debug_msg));
+    DecoderEvent(p, de_code, de_str, 1, 1);
+    p->sctph = NULL;
+    pc.discards++;
+    pc.sctpdisc++;
+
+    return;
+}
+
+/*
+ * Used often.  Better to make it a macro.  Why a macro and not an
+ * inlined function?  Because if inline, we have to pass quite a
+ * few arguments and fuss around with snprintf().  Macros can avoid
+ * this and only needs two arguments.
+ */
+
+/* Check for a bad chunk length. */
+#define CHECK_CHUNK_LENGTH(c,s)                                                     \
+    if (clen < SCTP_##c##_C_LEN)                                                    \
+    {                                                                               \
+        SctpChunkAnomaly(p, clen, "SCTP " #c " Chunk is smaller than " #s " bytes", \
+                         DECODE_SCTP_##c##_C_BAD_LEN,                               \
+                         DECODE_SCTP_##c##_C_BAD_LEN_STR);                          \
+        break;                                                                      \
+    }
+
+/* Make sure the <CHUNKT_TYPE> chunk's flags field is 0. */
+#define CHECK_CHUNK_FLAGS_ARE_ZERO(c)                                               \
+    if (cflags > 0)                                                                 \
+    {                                                                               \
+        SctpChunkAnomaly(p, clen, "SCTP " #c " chunk flags field "                  \
+                         "is non-zero", DECODE_SCTP_##c##_C_FLAGS_NZERO,            \
+                         DECODE_SCTP_##c##_C_FLAGS_NZERO_STR);                      \
+        break;                                                                      \
+    }
+
+
+void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
+{
+    uint8_t ctype = sctp_chunk->type;
+    uint8_t cflags = sctp_chunk->flags;
+    uint16_t clen = ntohs(sctp_chunk->length);
+
+    /* Check for zero-length chunk (minimum should ALWAYs be 4 bytes. */
+    if (clen == 0)
+    {
+        SctpChunkAnomaly(p, clen, "SCTP Chunk Length is 0 bytes",
+                         DECODE_SCTP_CHUNK_ZERO_LENGTH,
+                         DECODE_SCTP_CHUNK_ZERO_LENGTH_STR);
+        return;
+    }
+
+	/* Depending on the chunk type, check for certain anomalies, per the RFCs. */
+    switch (ctype)
+    {
+        /* DATA Chunk */
+        case SCTP_DATA_C:
+            printf("DEBUG: Got DATA chunk!\n");
+            CHECK_CHUNK_LENGTH(DATA, SCTP_DATA_C_LEN)
+
+            /*
+             * Make sure the DATA chunk's reserved area in flags is 0.
+             * Note: this includes in the 'I' flag defined in
+             * draft-tuexen-tsvwg-sctp-sack-immediately-06, so
+             * bitwise-AND 0xf0, not 0xf8.
+             */
+            /*
+             * FIXME: Create bitfield or macros to check the DATA flags, as
+             * well any flags on the other chunks.  Not for here, of course,
+             * but for future SCTP rule options.
+             */
+            if ((cflags & 0xf0) > 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP DATA chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_DATA_C_RSVD_NZERO,
+                                 DECODE_SCTP_DATA_C_RSVD_NZERO_STR);
+                break;
+            }
+            break;
+
+        /* INIT Chunk */
+        case SCTP_INIT_C:
+            printf("DEBUG: Got INIT chunk!\n");
+            CHECK_CHUNK_LENGTH(INIT, SCTP_INIT_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(INIT)
+
+            /*
+             * Per RFC4960:
+             *   - The initiate tag should not be 0 (error).
+             *   - For an INIT chunk, the verification tag MUST be zero.
+             *   - The value '0' cannot be used in either the # of outbound
+             *     streams or # of inbound streams fields.
+             */
+            if (ntohl(sctp_chunk->init.initiate_tag) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT Chunk and initiate tag is == 0",
+                                 DECODE_SCTP_INIT_C_ITAG_ZERO,
+                                 DECODE_SCTP_INIT_C_ITAG_ZERO_STR);
+                break;
+            }
+            else if (ntohl(p->sctph->sh_vtag) != 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT Chunk and verification tag is > 0",
+                                 DECODE_SCTP_INIT_C_VTAG_NZERO,
+                                 DECODE_SCTP_INIT_C_VTAG_NZERO_STR);
+                break;
+            }
+            else if (ntohs(sctp_chunk->init.num_out_streams) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT Chunk # of outbound streams"
+                                 "is zero", DECODE_SCTP_INIT_C_NOUTSTR_ZERO,
+                                 DECODE_SCTP_INIT_C_NOUTSTR_ZERO_STR);
+                break;
+            }
+            else if (ntohs(sctp_chunk->init.num_in_streams) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT Chunk # of inbound streams"
+                                 "is zero", DECODE_SCTP_INIT_C_NINSTR_ZERO,
+                                 DECODE_SCTP_INIT_C_NINSTR_ZERO_STR);
+                break;
+            }
+            break;
+
+        /* INIT ACK Chunk */
+        case SCTP_INIT_ACK_C:
+            printf("DEBUG: Got INIT_ACK chunk!\n");
+            CHECK_CHUNK_LENGTH(INIT_ACK, SCTP_INIT_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(INIT_ACK)
+
+            /*
+             * Per RFC4960:
+             *   - The initiate tag MUST NOT be 0.
+             *   - The value '0' cannot be used in either the # of outbound
+             *     streams or # of inbound streams fields.
+             */
+            if (ntohl(sctp_chunk->init_ack.initiate_tag) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT_ACK Chunk and initiate tag is == 0",
+                                 DECODE_SCTP_INIT_ACK_C_ITAG_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_ITAG_ZERO_STR);
+                break;
+            }
+            else if (ntohs(sctp_chunk->init_ack.num_out_streams) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT_ACK Chunk # of outbound streams"
+                                 "is zero", DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO_STR);
+                break;
+            }
+            else if (ntohs(sctp_chunk->init_ack.num_in_streams) == 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP INIT_ACK Chunk # of inbound streams"
+                                 "is zero", DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO,
+                                 DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO_STR);
+                break;
+            }
+            break;
+
+        /* SACK Chunk */
+        case SCTP_SACK_C:
+            printf("DEBUG: Got SACK chunk!\n");
+            CHECK_CHUNK_LENGTH(SACK, SCTP_SACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SACK)
+
+            break;
+
+        /* HEARTBEAT Chunk */
+        case SCTP_HEARTBEAT_C:
+            printf("DEBUG: Got HEARTBEAT chunk!\n");
+            CHECK_CHUNK_LENGTH(HEARTBEAT, SCTP_HEARTBEAT_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(HEARTBEAT)
+
+            break;
+
+        /* HEARTBEAT_ACK Chunk */
+        case SCTP_HEARTBEAT_ACK_C:
+            printf("DEBUG: Got HEARTBEAT_ACK chunk!\n");
+            CHECK_CHUNK_LENGTH(HEARTBEAT_ACK, SCTP_HEARTBEAT_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(HEARTBEAT_ACK)
+
+            break;
+
+        /* ABORT Chunk */
+        case SCTP_ABORT_C:
+            printf("DEBUG: Got ABORT chunk!\n");
+            CHECK_CHUNK_LENGTH(ABORT, SCTP_ABORT_C_LEN)
+
+            /* Make sure the ABORT chunk's reserved area in flags is 0. */
+            if ((cflags & 0xfe) > 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP ABORT chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_ABORT_C_RSVD_NZERO,
+                                 DECODE_SCTP_ABORT_C_RSVD_NZERO_STR);
+                break;
+            }
+
+            /* We do not check the Cause(es). */
+            /* XXX: Yet? */
+            break;
+
+        /* SHUTDOWN Chunk */
+        case SCTP_SHUTDOWN_C:
+            printf("DEBUG: Got SHUTDOWN chunk!\n");
+            CHECK_CHUNK_LENGTH(SHUTDOWN, SCTP_SHUTDOWN_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SHUTDOWN)
+
+            break;
+
+        /* SHUTDOWN_ACK Chunk */
+        case SCTP_SHUTDOWN_ACK_C:
+            printf("DEBUG: Got SHUTDOWN_ACK chunk!\n");
+            CHECK_CHUNK_LENGTH(SHUTDOWN_ACK, SCTP_SHUTDOWN_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(SHUTDOWN_ACK)
+
+            break;
+
+        /* ERROR Chunk */
+        case SCTP_ERROR_C:
+            printf("DEBUG: Got ERROR chunk!\n");
+            CHECK_CHUNK_LENGTH(ERROR, SCTP_ERROR_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ERROR)
+
+            /* We do not check the Cause(es). */
+            /* XXX: Yet? */
+            break;
+
+        /* COOKIE_ECHO Chunk */
+        case SCTP_COOKIE_ECHO_C:
+            printf("DEBUG: Got COOKIE_ECHO chunk!\n");
+            CHECK_CHUNK_LENGTH(COOKIE_ECHO, SCTP_COOKIE_ECHO_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(COOKIE_ECHO)
+
+            break;
+
+        /* COOKIE_ACK Chunk */
+        case SCTP_COOKIE_ACK_C:
+            printf("DEBUG: Got COOKIE_ACK chunk!\n");
+            CHECK_CHUNK_LENGTH(COOKIE_ACK, SCTP_COOKIE_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(COOKIE_ACK)
+
+            break;
+
+        /* ECNE Chunk */
+        case SCTP_ECNE_C:
+            printf("DEBUG: Got ECNE chunk!\n");
+            CHECK_CHUNK_LENGTH(ECNE, SCTP_ECNE_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ECNE)
+
+            break;
+
+        /* CWR Chunk */
+        case SCTP_CWR_C:
+            printf("DEBUG: Got CWR chunk!\n");
+            CHECK_CHUNK_LENGTH(CWR, SCTP_CWR_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(CWR)
+
+            break;
+
+        /* SHUTDOWN_COMPLETE Chunk */
+        case SCTP_SHUTDOWN_COMPLETE_C:
+            printf("DEBUG: Got SHUTDOWN_COMPLETE chunk!\n");
+            CHECK_CHUNK_LENGTH(SHUTDOWN_COMPLETE, SCTP_SHUTDOWN_COMPLETE_C_LEN)
+
+            /* Make sure the SHUTDOWN_COMPLETE chunk's reserved area in flags is 0. */
+            if ((cflags & 0xfe) > 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP SHUTDOWN_COMPLETE chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO,
+                                 DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO_STR);
+                break;
+            }
+            break;
+
+        /* AUTH Chunk */
+        case SCTP_AUTH_C:
+            printf("DEBUG: Got AUTH chunk!\n");
+            CHECK_CHUNK_LENGTH(AUTH, SCTP_AUTH_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(AUTH)
+
+            break;
+
+        /* ASCONF_ACK Chunk */
+        case SCTP_ASCONF_ACK_C:
+            printf("DEBUG: Got ASCONF_ACK chunk!\n");
+            CHECK_CHUNK_LENGTH(ASCONF_ACK, SCTP_ASCONF_ACK_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ASCONF_ACK)
+
+            break;
+
+        /* PKTDROP Chunk */
+        case SCTP_PKTDROP_C:
+            printf("DEBUG: Got PKTDROP chunk!\n");
+            CHECK_CHUNK_LENGTH(PKTDROP, SCTP_PKTDROP_C_LEN)
+
+            /* Make sure the PKTDROP chunk's reserved area in flags is 0. */
+            if ((cflags & 0xf0) > 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP PKTDROP chunk flags reserved field "
+                                 "is non-zero", DECODE_SCTP_PKTDROP_C_FLAGS_NZERO,
+                                 DECODE_SCTP_PKTDROP_C_FLAGS_NZERO_STR);
+                break;
+            }
+            else if (ntohs(sctp_chunk->pktdrop.reserved) != 0)
+            {
+                SctpChunkAnomaly(p, clen, "SCTP PKTDROP Chunk reserved field "
+                                 "is non-zero", DECODE_SCTP_PKTDROP_C_RSVD_NZERO,
+                                 DECODE_SCTP_PKTDROP_C_RSVD_NZERO_STR);
+                break;
+            }
+            break;
+
+        /* RE-CONFIG Chunk */
+        case SCTP_RECONFIG_C:
+            printf("DEBUG: Got RE-CONFIG chunk!\n");
+            CHECK_CHUNK_LENGTH(RECONFIG, SCTP_RECONFIG_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(RECONFIG)
+
+            break;
+
+        /* PAD Chunk */
+        case SCTP_PAD_C:
+            printf("DEBUG: Got PAD chunk!\n");
+            CHECK_CHUNK_LENGTH(PAD, SCTP_PAD_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(PAD)
+
+            break;
+
+        /* FORWARD_TSN Chunk */
+        case SCTP_FORWARD_TSN_C:
+            printf("DEBUG: Got FORWARD_TSN chunk!\n");
+            CHECK_CHUNK_LENGTH(FORWARD_TSN, SCTP_FORWARD_TSN_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(FORWARD_TSN)
+
+            break;
+
+        /* ASCONF Chunk */
+        case SCTP_ASCONF_C:
+            printf("DEBUG: Got ASCONF chunk!\n");
+            CHECK_CHUNK_LENGTH(ASCONF, SCTP_ASCONF_C_LEN)
+            CHECK_CHUNK_FLAGS_ARE_ZERO(ASCONF)
+
+            break;
+
+        /* XXX: default catch-all for unknown chunk types. */
+    }
+}
+
+
+/**
+ * SctpGetNextChunk - Gets the next chunk from the packet.
+ * @c: Pointer to the current SctpChunk structure containing chunk data.
+ * @rlen: uint32_t value of the remaining payload length.
+ * @clen: uint16_t value of the current chunk's length.
+ *
+ * Returns the new remaining length in the packet and points c to the
+ * next chunk in the packet.  If there are no more chunks left, it will
+ * return 0 and c will be set to NULL.
+ */
+inline uint32_t SctpGetNextChunk(SctpChunk *c, uint32_t rlen, uint16_t clen)
+{
+    if (rlen > 0)
+    {
+        /*
+         * If remaining_len is > 0, see if it is > SCTP_MAX_PADDING_LEN.  If
+         * so, we probably have another chunk to process.  Else, force
+         * remaining_len to 0, because it is most likely padding bytes.  Per
+         * RFC4960, all SCTP chunks MUST be padded to a 4-byte boundary, but
+         * this padding value MUST NOT be included in the chunk length field
+         * AND this padding MUST NOT exceed 3 bytes.
+         */
+        if (rlen > SCTP_MAX_PADDING_LEN)
+            c = (SctpChunk *)((unsigned char *)c + clen);
+        else
+        {
+            rlen = 0;
+            c = NULL;
+        }
+    }
+    
+    return rlen;
+}
+
+
+/**
+ * SctpGetNextCause - Gets the next cause from the chunk.
+ * @c: Pointer to the current SctpCause structure containing cause data.
+ * @rclen: uint16_t value of the remaining chunk length.
+ * @clen: uint16_t value of the current cause's length.
+ *
+ * Returns the new remaining length in the chunk and points c to the
+ * next cause in the chunk.  If there are no more causes left, it will
+ * return 0 and c will be set to NULL.
+ */
+inline uint16_t SctpGetNextCause(SctpCause *c, uint16_t rclen, uint16_t clen)
+{
+    if (rclen > 0)
+    {
+        /* Same as in SctpGetNextChunk. */
+        if (rclen > SCTP_MAX_PADDING_LEN)
+            c = (SctpCause *)((unsigned char *)c + clen);
+        else
+        {
+            rclen = 0;
+            c = NULL;
+        }
+    }
+    
+    return rclen;
+}
+
+
+/**
+ * SctpGetNextParam - Gets the next parameter from the chunk.
+ * @p: Pointer to the current SctpParam structure containing param data.
+ * @rclen: uint16_t value of the remaining chunk length.
+ * @plen: uint16_t value of the current param's length.
+ *
+ * Returns the new remaining length in the chunk and points p to the
+ * next param in the chunk.  If there are no more params left, it will
+ * return 0 and p will be set to NULL.
+ */
+inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen)
+{
+    if (rclen > 0)
+    {
+        /* Same as in SctpGetNextChunk. */
+        if (rclen > SCTP_MAX_PADDING_LEN)
+            p = (SctpParam *)((unsigned char *)p + plen);
+        else
+        {
+            rclen = 0;
+            p = NULL;
+        }
+    }
+    
+    return rclen;
+}
+
+
+/*
+ * Function: DecodeSCTP(uint8_t *, const uint32_t, Packet *)
+ *
+ * Purpose: Decode the SCTP transport layer
+ *
+ * Arguments: pkt => ptr to the packet data
+ *            len => length from here to the end of the packet
+ *            p   => pointer to dummy packet decode struct
+ *
+ * Returns: void function
+ */
+ void DecodeSCTP(const uint8_t * pkt, const uint32_t len, Packet * p)
+ {
+    if (len < SCTP_COMMON_HEADER_LEN)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+            "Truncated SCTP common header (%d bytes)\n", len));
+
+        DecoderEvent(p, DECODE_SCTP_DGRAM_LT_SCTPHDR,
+                        DECODE_SCTP_DGRAM_LT_SCTPHDR_STR, 1, 1);
+
+        p->sctph = NULL;
+        pc.discards++;
+        pc.sctpdisc++;
+
+        return;
+    }
+
+	/* Overlay the SCTP header on top of the data. */
+    p->sctph = (SCTPHdr *) pkt;
+
+    /*
+     * Checksum code moved in front of the other decoder alerts.  If it's a
+     * bad checksum (maybe due to encrypted ESP traffic), the other alerts
+     * could be false positives.
+     */
+    if (ScSctpChecksums())
+    {
+        uint32_t csum_pkt = ntohl(p->sctph->sh_csum);
+        uint32_t csum_calc;
+        uint32_t csum_valid = 0;
+
+        /*
+         * RFC2960, SCTP's original RFC, origially used the Adler32 algorithm
+         * to generate the checksum.  However, on small packets, this was
+         * inadequate and RFC3309 updated RFC2960 to use the CRC32 algorithm
+         * for the checksum instead.  Both have now been superceded by RFC4960.
+         *
+         * Start by generating only the CRC32 checksum.  If that is invalid,
+         * then we will compute the Adler32 checksum and re-compare.
+         */
+        uint32_t csum_crc32 = SctpCrc32Chksum((unsigned char *)p->sctph, len);
+
+        if ((csum_crc32 == 0) || (csum_crc32 != csum_pkt))
+        {
+            /* Try Adler32. */
+            uint32_t csum_adler32 = SctpAdler32Chksum((unsigned char *)p->sctph, len);
+
+            if ((csum_adler32 != 0) || (csum_adler32 == csum_pkt))
+            {
+                csum_valid = 1;
+                csum_calc = csum_adler32;
+
+                DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+                    "Checksum type is Adler32!\n"));
+                
+                DecoderEvent(p, DECODE_SCTP_CSUM_IS_ADLER32,
+                                DECODE_SCTP_CSUM_IS_ADLER32_STR, 1, 1);
+            }
+        }
+        else
+        {
+            csum_valid = 1;
+            csum_calc = csum_crc32;
+        }
+
+        if (!csum_valid)
+        {
+            /*
+             * Don't drop the packet if this is encapuslated in Teredo or ESP.
+             * Just get rid of the SCTP header and stop decoding.
+             */
+            if (p->packet_flags & PKT_UNSURE_ENCAP)
+            {
+                p->sctph = NULL;
+                pc.discards++;
+                pc.sctpdisc++;
+
+                return;
+            }
+
+            p->error_flags |= PKT_ERR_CKSUM_SCTP;
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Bad SCTP checksum\n",
+                                    "0x%x versus 0x%x\n", csum_calc,
+                                    csum_pkt););
+
+            if (ScIdsMode())
+                queueExecDrop(execSctpChksmDrop, p);
+        }
+        else
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE,"SCTP Checksum: OK\n"););
+    }
+
+    /* Fill in the printout data structs */
+    p->sp = ntohs(p->sctph->sh_sport);
+    p->dp = ntohs(p->sctph->sh_dport);
+
+    DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "SCTP header starts at: %p\n", p->sctph););
+
+    PushLayer(PROTO_SCTP, p, pkt, SCTP_COMMON_HEADER_LEN);
+    p->data = (uint8_t *) (pkt + SCTP_COMMON_HEADER_LEN);
+    p->dsize = (u_short)(len - SCTP_COMMON_HEADER_LEN);
+    p->proto_bits |= PROTO_BIT__SCTP;
+
+    /* Drop packet if we ignore this port */
+    if (ScIgnoreSctpPort(p->sp) || ScIgnoreSctpPort(p->dp))
+    {
+        /* Ignore all preprocessors for this packet */
+        p->packet_flags |= PKT_IGNORE_PORT;
+        return;
+    }
+
+    /* Now we check various features of the chunks. */
+    SctpChunk *chunk_data = (SctpChunk *)p->sctph->chunks;
+    uint16_t chunk_length = 0;
+    uint32_t remaining_len = (len - SCTP_COMMON_HEADER_LEN);
+
+    /*
+     * Loop through the remainder of the SCTP payload and check all chunks for
+     * decoder anomalies, based on RFC4960's guidelines. Chunk bundling is
+     * explained in RFC4960, Section 6.10.
+     */
+    while ((chunk_data != NULL) && (remaining_len != 0))
+    {
+        SctpChunkTests(p, chunk_data);
+        chunk_length = SCTP_ADD_PADDING(ntohs(chunk_data->length));
+        
+        /*
+         * If the chunk length is somehow greater than the length of the
+         * remainder of the packet, something isn't right.  Flag a decoder
+         * alert.
+         */
+        if (remaining_len >= chunk_length)
+            remaining_len -= chunk_length;
+        else
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
+                "SCTP chunk length (%d bytes) is > remaining packet "
+                "length (%d bytes)\n", chunk_length, remaining_len));
+
+            DecoderEvent(p, DECODE_SCTP_CHUNK_LEN_GT_DGRAM,
+                            DECODE_SCTP_CHUNK_LEN_GT_DGRAM_STR, 1, 1);
+
+            p->sctph = NULL;
+            pc.discards++;
+            pc.sctpdisc++;
+
+            return;
+        }
+
+        /* Get the next chunk. */
+        remaining_len = SctpGetNextChunk(chunk_data, remaining_len, chunk_length);
+        
+        /* Increment the chunk count.  Used by various SCTP rule options. */
+        p->sctp_chunk_count++;
+    }
+ }
+
+/*
  * Function: DecodeIPV6(uint8_t *, uint32_t)
  *
  * Purpose: Decoding IPv6 headers
@@ -3627,6 +4327,11 @@ void DecodeIPV6Extensions(uint8_t next, const uint8_t *pkt, uint32_t len, Packet
             CheckIPv6ExtensionOrder(p);
             DecodeUDP(pkt, len, p);
             return;
+        case IPPROTO_SCTP:
+            pc.sctp6++;
+            CheckIPv6ExtensionOrder(p);
+            DecodeSCTP(pkt, len, p);
+            return;
         case IPPROTO_ICMPV6:
             pc.icmp6++;
             CheckIPv6ExtensionOrder(p);
@@ -4322,6 +5027,12 @@ void DecodeESP(const uint8_t *pkt, uint32_t len, Packet *p)
             p->packet_flags &= ~PKT_UNSURE_ENCAP;
             return;
 
+        case IPPROTO_SCTP:
+            pc.sctp++;
+            DecodeSCTP(esp_payload, len, p);
+            p->packet_flags &= ~PKT_UNSURE_ENCAP;
+            return;
+
         case IPPROTO_ICMP:
             pc.icmp++;
             DecodeICMP(esp_payload, len, p);
@@ -5095,6 +5806,9 @@ void DecodeTCP(const uint8_t * pkt, const uint32_t len, Packet * p)
             if (p->packet_flags & PKT_UNSURE_ENCAP)
             {
                 p->tcph = NULL;
+                pc.discards++;
+                pc.tdisc++;
+
                 return;
             }
 
diff --git a/src/decode.h b/src/decode.h
index c664e9e..3a9dfd7 100644
--- a/src/decode.h
+++ b/src/decode.h
@@ -379,6 +379,107 @@ struct enc_header {
 #define DHCP_CLIENT_PORT    68
 #define DHCP_SERVER_PORT    67
 
+
+/*
+ * SCTP Definitions
+ */
+
+/* SCTP Chunk types. */
+#define SCTP_DATA_C                 0x00    /* DATA chunk (RFC4960) */
+#define SCTP_INIT_C                 0x01    /* INIT chunk (RFC4960) */
+#define SCTP_INIT_ACK_C             0x02    /* INIT-ACK chunk (RFC4960) */
+#define SCTP_SACK_C                 0x03    /* SACK chunk (RFC4960) */
+#define SCTP_HEARTBEAT_C            0x04    /* HEARTBEAT chunk (RFC4960) */
+#define SCTP_HEARTBEAT_ACK_C        0x05    /* HEARTBEAT-ACK chunk (RFC4960) */
+#define SCTP_ABORT_C                0x06    /* ABORT chunk (RFC4960) */
+#define SCTP_SHUTDOWN_C             0x07    /* SHUTDOWN chunk (RFC4960) */
+#define SCTP_SHUTDOWN_ACK_C         0x08    /* SHUTDOWN-ACK chunk (RFC4960) */
+#define SCTP_ERROR_C                0x09    /* ERROR chunk (RFC4960) */
+#define SCTP_COOKIE_ECHO_C          0x0a    /* COOKIE-ECHO chunk (RFC4960) */
+#define SCTP_COOKIE_ACK_C           0x0b    /* COOKIE-ACK chunk (RFC4960) */
+#define SCTP_ECNE_C                 0x0c    /* ECNE (Explicit Congestion Notification) chunk (RFC4960) */
+#define SCTP_CWR_C                  0x0d    /* CWR (Congestion Window Reduced) chunk (RFC4960) */
+#define SCTP_SHUTDOWN_COMPLETE_C    0x0e    /* SHUTDOWN-COMPLETE chunk (RFC4960) */
+#define SCTP_AUTH_C                 0x0f    /* AUTH chunk (RFC4895) */
+#define SCTP_ASCONF_ACK_C           0x80    /* ASCONF-ACK chunk (RFC5061) */
+#define SCTP_PKTDROP_C              0x81    /* PKTDROP chunk (draft-stewart-sctp-pktdrprep-12) */
+#define SCTP_RECONFIG_C             0x82    /* RE-CONFIG chunk (draft-ietf-tsvwg-sctp-strrst-12) */
+#define SCTP_PAD_C                  0x84    /* PAD chunk (RFC4820) */
+#define SCTP_FORWARD_TSN_C          0xc0    /* FORWARD-TSN chunk (RFC3758) */
+#define SCTP_ASCONF_C               0xc1    /* ASCONF chunk (RFC5061) */
+
+/* SCTP header lengths (minimum, anyways). */
+#define SCTP_COMMON_HEADER_LEN      12      /* SCTP common header is 12 bytes */
+#define SCTP_CAUSE_HEADER_LEN        4      /* SCTP cause header length is 4 bytes */
+#define SCTP_PARAM_HEADER_LEN        4      /* SCTP parameter header length is 4 bytes */
+#define SCTP_MAX_PADDING_LEN         3      /* Maximum amount of padding allowed to align data. */
+#define SCTP_DATA_C_LEN             17      /* Minimum size of a DATA (0x00) chunk */
+#define SCTP_INIT_C_LEN             20      /* Minimum size of an INIT (0x01) chunk */
+#define SCTP_INIT_ACK_C_LEN         20      /* Same as INIT, except type is 0x02) */
+#define SCTP_SACK_C_LEN             16      /* Minimum size of a SACK (0x03) chunk */
+#define SCTP_HEARTBEAT_C_LEN         8      /* Minimum size of a HEARTBEAT (0x04) chunk */
+#define SCTP_HEARTBEAT_ACK_C_LEN     8      /* Minimum size of a HEARTBEAT ACK (0x05) chunk */
+#define SCTP_ABORT_C_LEN             4      /* Minimum size of an ABORT (0x06) chunk */
+#define SCTP_SHUTDOWN_C_LEN          8      /* Minimum size of a SHUTDOWN (0x07) chunk */
+#define SCTP_SHUTDOWN_ACK_C_LEN      4      /* Minimum size of a SHUTDOWN ACK (0x08) chunk */
+#define SCTP_ERROR_C_LEN             8      /* Minimum size of an ERROR (0x09) chunk */
+#define SCTP_COOKIE_ECHO_C_LEN       4      /* Minimum size of a COOKIE ECHO (0x0a) chunk */
+#define SCTP_COOKIE_ACK_C_LEN        4      /* Minimum size of a COOKIE ACK (0x0b) chunk */
+#define SCTP_ECNE_C_LEN              8      /* Minimum size of an ECNE (0x0c) chunk */
+#define SCTP_CWR_C_LEN               8      /* Minimum size of a CWR (0x0d) chunk */
+#define SCTP_SHUTDOWN_COMPLETE_C_LEN 4      /* Minimum size of a SHUTDOWN COMPLETE (0x0e) chunk */
+#define SCTP_AUTH_C_LEN              4      /* Minimum size of an AUTH (0x0f) chunk */
+#define SCTP_ASCONF_ACK_C_LEN        8      /* Minimum size of an ASCONF ACK (0x80) chunk */
+#define SCTP_PKTDROP_C_LEN          16      /* Minimum size of a PKTDROP (0x81) chunk */
+#define SCTP_RECONFIG_C_LEN          4      /* Minimim size of a RE-CONFIG (0x82) chunk */
+#define SCTP_PAD_C_LEN               4      /* Minimum size of a PAD (0x84) chunk */
+#define SCTP_FORWARD_TSN_C_LEN      12      /* Minimum size of a FORWARD TSN (0xc0) chunk */
+#define SCTP_ASCONF_C_LEN           12      /* Minimum size of an ASCONF (0xc1) chunk */
+
+/* SCTP Parameter Types. */
+#define SCTP_HEARTBEAT_INFO_P       0x0001  /* Heartbeat Info */
+    /* 0x0002 - 0x0004 Unassigned */
+#define SCTP_IPV4_ADDR_P            0x0005  /* IPv4 Address */
+#define SCTP_IPV6_ADDR_P            0x0006  /* IPv6 Address */
+#define SCTP_STATE_COOKIE_P         0x0007  /* State Cookie */
+#define SCTP_UNKNOWN_P              0x0008  /* Unrecognized Parameters */
+#define SCTP_COOKIE_PRESERVE_P      0x0009  /* Cookie Preservative */
+#define SCTP_HOSTNAME_ADDR_P        0x000b  /* Host Name Address */
+#define SCTP_SUPP_ADDRTYPES_P       0x000c  /* Supported Address Types */
+    /* draft-ietf-tsvwg-sctp-strrst-12 */
+#define SCTP_OUT_SSN_RST_REQ_P      0x000d  /* Outgoing SSN Reset Request */
+#define SCTP_IN_SSN_RST_REQ_P       0x000e  /* Incoming SSN Reset Request */
+#define SCTP_SSNTSN_RST_REQ_P       0x000f  /* SSN/TSN Reset Request */
+#define SCTP_RECONFIG_RESP_P        0x0010  /* Re-configuration Response */
+#define SCTP_ADD_OUT_STRM_REQ_P     0x0011  /* Add Outgoing Streams Request */
+#define SCTP_ADD_IN_STRM_REQ_P      0x0012  /* Add Incoming Streams Request */
+    /* 0x0013 = 0x7fff Unassigned */
+#define SCTP_ECN_CAPABLE_P          0x8000  /* ECN Capable (Reserved) */
+#define SCTP_NONCE_SUPPORTED_P      0x8001  /* Nonce-Supported */
+#define SCTP_RANDOM_P               0x8002  /* Random */
+#define SCTP_CHUNK_LIST_P           0x8003  /* Chunk List */
+#define SCTP_REQ_HMAC_ALGO_P        0x8004  /* Requested HMAC Algorithm Parameter */
+#define SCTP_PADDING_P              0x8005  /* Padding */
+#define SCTP_SUPP_EXTEN_P           0x8008  /* Supported Extensions */
+    /* 0x8009 - 0xbfff Unassigned */
+#define SCTP_SUPP_FWD_TSN_P         0xc000  /* Forward TSN Supported */
+#define SCTP_ADD_IP_P               0xc001  /* Add IP */
+#define SCTP_DEL_IP_P               0xc002  /* Delete IP */
+#define SCTP_SUPP_ERR_CAUSE_P       0xc003  /* Error Cause Indication */
+#define SCTP_PRIMARY_ADDR_P         0xc004  /* Set Primary Address */
+#define SCTP_SUCCESS_P              0xc005  /* Success Indication */
+#define SCTP_ADAPT_LAYER_P          0xc006  /* Adaptation Layer Indication */
+
+/* SCTP HMAC Algorithm bits. */
+#define SCTP_SHA1_HMAC              0x0001  /* HMAC SHA-1 */
+#define SCTP_SHA256_HMAC            0x0003  /* HMAC SHA-256 */
+
+/* Misc SCTP Bits.  Borrowed from Wireshark. */
+#define SCTP_ADD_PADDING(x) ((((x) + 3) >> 2) << 2)
+
+/* End SCTP definitions */
+
+
 #ifndef NO_NON_ETHER_DECODER
 /* Start Token Ring */
 #define TR_ALEN             6        /* octets in an Ethernet header */
@@ -661,8 +762,9 @@ typedef enum {
 #define PKT_ERR_CKSUM_UDP    0x04
 #define PKT_ERR_CKSUM_ICMP   0x08
 #define PKT_ERR_CKSUM_IGMP   0x10
-#define PKT_ERR_CKSUM_ANY    0x1F
-#define PKT_ERR_BAD_TTL      0x20
+#define PKT_ERR_CKSUM_SCTP   0x20
+#define PKT_ERR_CKSUM_ANY    0x3F
+#define PKT_ERR_BAD_TTL      0x40
 
 /*  D A T A  S T R U C T U R E S  *********************************************/
 typedef int (*LogFunction)(void *ssnptr, uint8_t **buf, uint32_t *len, uint32_t *type);
@@ -1541,6 +1643,227 @@ typedef struct _EtherARP
 }         EtherARP;
 
 
+/*
+ * SCTP data structures
+ */
+#ifdef _MSC_VER
+  /* Visual C++ pragma to disable warning
+   * messages about nonstandard bit field type
+   */
+  #pragma warning( disable : 4214 )
+#endif
+
+#pragma pack(push)  /* push current alignment to stack */
+#pragma pack(1)     /* set alignment to 1 byte boundary */
+
+typedef struct _SCTPHdr
+{
+    uint16_t sh_sport;        /* Source port */
+    uint16_t sh_dport;        /* Destination port */
+    uint32_t sh_vtag;         /* Verification tag */
+    uint32_t sh_csum;         /* Checksum (CRC32c/Adler32) */
+    uint8_t chunks[0];        /* Start of SCTP Chunks */
+} SCTPHdr;
+
+/* Sctp Cause Header (for the ABORT/ERROR chunks). */
+typedef struct _SctpCause
+{
+    uint16_t code;
+    uint16_t length;
+    uint8_t info[0];
+} SctpCause;
+
+/* Sctp Param Header. */
+typedef struct _SctpParam
+{
+    uint16_t type;
+    uint16_t length;
+    uint8_t value[0];
+} SctpParam;
+
+/* Sctp Chunk Type Definitions. */
+typedef struct _SctpDataChunk
+{
+    uint32_t tsn;
+    uint16_t stream_id;
+    uint16_t stream_seq;
+    uint32_t payload_id;
+    uint8_t data[0];
+} SctpDataChunk;
+
+typedef struct _SctpInitChunk
+{
+    uint32_t initiate_tag;
+    uint32_t a_rwnd;
+    uint16_t num_out_streams;
+    uint16_t num_in_streams;
+    uint32_t tsn;
+    uint8_t params[0];
+} SctpInitChunk;
+
+typedef struct _SctpInitAckChunk
+{
+    uint32_t initiate_tag;
+    uint32_t a_rwnd;
+    uint16_t num_out_streams;
+    uint16_t num_in_streams;
+    uint32_t tsn;
+    uint8_t params[0];
+} SctpInitAckChunk;
+
+typedef struct _SctpSackChunk
+{
+    uint32_t cuml_tsn_ack;
+    uint32_t a_rwnd;
+    uint16_t num_gap_acks;
+    uint16_t num_dupe_tsns;
+    uint8_t data[0];
+} SctpSackChunk;
+
+typedef struct _SctpHeartbeatChunk
+{
+    uint8_t info[0];
+} SctpHeartbeatChunk;
+
+typedef struct _SctpHeartbeatAckChunk
+{
+    uint8_t info[0];
+} SctpHeartbeatAckChunk;
+
+typedef struct _SctpAbortChunk
+{
+    uint8_t causes[0];
+} SctpAbortChunk;
+
+typedef struct _SctpShutdownChunk
+{
+    uint32_t tsn_ack;
+} SctpShutdownChunk;
+
+typedef struct _SctpErrorChunk
+{
+    uint8_t causes[0];
+} SctpErrorChunk;
+
+typedef struct _SctpCookieEchoChunk
+{
+    char *cookie;
+} SctpCookieEchoChunk;
+
+typedef struct _SctpEcneChunk
+{
+    uint32_t lowest_tsn;
+} SctpEcneChunk;
+
+typedef struct _SctpCwrChunk
+{
+    uint32_t lowest_tsn;
+} SctpCwrChunk;
+
+typedef struct _SctpAuthChunk
+{
+    uint16_t skey_id;
+    uint16_t hmac_id;
+    uint8_t hmac[0];
+    uint8_t params[0];
+} SctpAuthChunk;
+
+typedef struct _SctpAsconfAckChunk
+{
+    uint32_t seq;
+    uint8_t params[0];
+} SctpAsconfAckChunk;
+
+typedef struct _SctpPktdropChunk
+{
+    uint32_t lbmr;
+    uint32_t queue_size;
+    uint16_t trunc_length;
+    uint16_t reserved;
+    uint8_t dropped_pkt[0];
+} SctpPktdropChunk;
+
+typedef struct _SctpReConfigChunk
+{
+    uint8_t params[0];
+} SctpReConfigChunk;
+
+typedef struct _SctpPadChunk
+{
+    uint8_t padding[0];
+} SctpPadChunk;
+
+typedef struct _SctpForwardTsnChunk
+{
+    uint32_t new_tsn;
+    uint8_t streams[0];
+} SctpForwardTsnChunk;
+
+typedef struct _SctpAsconfChunk
+{
+    uint32_t seq;
+    /* One IPv4 or IPv6 addr param is mandatory, but start at params[0]. */
+    uint8_t params[0];
+} SctpAsconfChunk;
+
+
+/* Sctp Chunk structure. */
+typedef struct _SctpChunk
+{
+    uint8_t type;      /* chunk type */
+    uint8_t flags;     /* chunk flags */
+    uint16_t length;   /* chunk length */
+
+    union
+    {
+        SctpDataChunk data;
+        SctpInitChunk init;
+        SctpInitAckChunk init_ack;
+        SctpSackChunk sack;
+        SctpHeartbeatChunk heartbeat;
+        SctpHeartbeatAckChunk heartbeat_ack;
+        SctpAbortChunk abort;
+        SctpShutdownChunk shutdown;
+        /* No struct for shutdown_ack. */
+        SctpErrorChunk error;
+        SctpCookieEchoChunk cookie_echo;
+        /* No struct for cookie_ack. */
+        SctpEcneChunk ecne;
+        SctpEcneChunk cwr;
+        /* No struct for shutdown_complete. */
+        SctpAuthChunk auth;
+        SctpAsconfAckChunk asconf_ack;
+        SctpPktdropChunk pktdrop;
+        SctpReConfigChunk reconfig;
+        SctpPadChunk pad;
+        SctpForwardTsnChunk forward_tsn;
+        SctpAsconfChunk asconf;
+    };
+} SctpChunk;
+
+#pragma pack(pop)   /* restore original alignment from stack */
+
+
+#ifdef _MSC_VER
+  /* Visual C++ pragma to enable warning messages
+   * about nonstandard bit field type
+   */
+  #pragma warning( default : 4214 )
+#endif
+/* End SCTP data structures */
+
+
+/*
+ * SCTP prototypes for utility functions used in other modules.
+ */
+
+inline uint32_t SctpGetNextChunk(SctpChunk *c, uint32_t rlen, uint16_t clen);
+inline uint16_t SctpGetNextCause(SctpCause *c, uint16_t rclen, uint16_t clen);
+inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen);
+
+/* End SCTP prottypes */
+
+
 #ifndef NO_NON_ETHER_DECODER
 typedef struct _EtherEapol
 {
@@ -1678,6 +2001,7 @@ typedef struct _Packet
     const UDPHdr *udph, *orig_udph;
     const UDPHdr *inner_udph;   /* if Teredo + UDP, this will be the inner UDP header */
     const UDPHdr *outer_udph;   /* if Teredo + UDP, this will be the outer UDP header */
+    const SCTPHdr *sctph, *orig_sctph;
     const ICMPHdr *icmph, *orig_icmph;
 
     const uint8_t *data;        /* packet payload pointer */
@@ -1722,6 +2046,7 @@ typedef struct _Packet
     uint32_t preprocessor_bits; /* flags for preprocessors to check */
     uint32_t preproc_reassembly_pkt_bits;
 
+    uint32_t sctp_chunk_count;  /* Counter for # of SCTP chunks in an SCTP packet. */
     uint32_t http_pipeline_count; /* Counter for HTTP pipelined requests */
     uint32_t packet_flags;      /* special flags for the packet */
 
@@ -1744,10 +2069,10 @@ typedef struct _Packet
     uint16_t tcp_options_len;
 
     //vvv-----------------------------
-    uint16_t sp;                /* source port (TCP/UDP) */
-    uint16_t dp;                /* dest port (TCP/UDP) */
-    uint16_t orig_sp;           /* source port (TCP/UDP) of original datagram */
-    uint16_t orig_dp;           /* dest port (TCP/UDP) of original datagram */
+    uint16_t sp;                /* source port (TCP/UDP/SCTP) */
+    uint16_t dp;                /* dest port (TCP/UDP/SCTP) */
+    uint16_t orig_sp;           /* source port (TCP/UDP/SCTP) of original datagram */
+    uint16_t orig_dp;           /* dest port (TCP/UDP/SCTP) of original datagram */
     //^^^-----------------------------
     // and so on ...
 
@@ -1831,14 +2156,16 @@ typedef struct _Packet
 #define PROTO_BIT__ARP      0x0002
 #define PROTO_BIT__TCP      0x0004
 #define PROTO_BIT__UDP      0x0008
-#define PROTO_BIT__ICMP     0x0010
-#define PROTO_BIT__TEREDO   0x0020
-#define PROTO_BIT__GTP      0x0040
+#define PROTO_BIT__SCTP     0x0010
+#define PROTO_BIT__ICMP     0x0020
+#define PROTO_BIT__TEREDO   0x0040
+#define PROTO_BIT__GTP      0x0080
 #define PROTO_BIT__ALL      0xffff
 
 #define IsIP(p) (IPH_IS_VALID(p))
 #define IsTCP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_TCP))
 #define IsUDP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_UDP))
+#define IsSCTP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_SCTP))
 #define IsICMP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_ICMP))
 #define GET_PKT_SEQ(p) (ntohl(p->tcph->th_seq))
 
@@ -1948,6 +2275,7 @@ void DecodeIP(const uint8_t *, const uint32_t, Packet *);
 void DecodeIPV6(const uint8_t *, uint32_t, Packet *);
 void DecodeTCP(const uint8_t *, const uint32_t, Packet *);
 void DecodeUDP(const uint8_t *, const uint32_t, Packet *);
+void DecodeSCTP(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMP(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMP6(const uint8_t *, const uint32_t, Packet *);
 void DecodeICMPEmbeddedIP(const uint8_t *, const uint32_t, Packet *);
diff --git a/src/detection-plugins/Makefile.am b/src/detection-plugins/Makefile.am
index 94a3c80..f8cc24f 100644
--- a/src/detection-plugins/Makefile.am
+++ b/src/detection-plugins/Makefile.am
@@ -61,7 +61,12 @@ sp_urilen_check.c sp_urilen_check.h \
 sp_file_data.c sp_file_data.h \
 sp_base64_decode.c sp_base64_decode.h \
 sp_base64_data.c sp_base64_data.h \
-sp_pkt_data.c sp_pkt_data.h
+sp_pkt_data.c sp_pkt_data.h \
+sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+sp_sctp_cause_code.c sp_sctp_cause_code.h
 
 copy_files = \
 	if test -f $$dst_file; then \
diff --git a/src/detection-plugins/Makefile.in b/src/detection-plugins/Makefile.in
index bf85df2..e00df62 100644
--- a/src/detection-plugins/Makefile.in
+++ b/src/detection-plugins/Makefile.in
@@ -88,7 +88,12 @@ am__libspd_a_SOURCES_DIST = detection_options.c detection_options.h \
 	sp_tcp_win_check.h sp_ttl_check.c sp_ttl_check.h \
 	sp_urilen_check.c sp_urilen_check.h sp_file_data.c \
 	sp_file_data.h sp_base64_decode.c sp_base64_decode.h \
-	sp_base64_data.c sp_base64_data.h sp_pkt_data.c sp_pkt_data.h
+	sp_base64_data.c sp_base64_data.h sp_pkt_data.c sp_pkt_data.h \
+	sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+	sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+	sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+	sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+	sp_sctp_cause_code.c sp_sctp_cause_code.h
 @BUILD_REACT_TRUE@am__objects_1 = sp_react.$(OBJEXT)
 @BUILD_RESPOND3_TRUE@am__objects_2 = sp_respond3.$(OBJEXT)
 am_libspd_a_OBJECTS = detection_options.$(OBJEXT) sp_asn1.$(OBJEXT) \
@@ -110,7 +115,9 @@ am_libspd_a_OBJECTS = detection_options.$(OBJEXT) sp_asn1.$(OBJEXT) \
 	sp_tcp_win_check.$(OBJEXT) sp_ttl_check.$(OBJEXT) \
 	sp_urilen_check.$(OBJEXT) sp_file_data.$(OBJEXT) \
 	sp_base64_decode.$(OBJEXT) sp_base64_data.$(OBJEXT) \
-	sp_pkt_data.$(OBJEXT)
+	sp_pkt_data.$(OBJEXT) sp_sctp_chunk_type.$(OBJEXT) \
+	sp_sctp_chunk_flags.$(OBJEXT) sp_sctp_chunk_field.$(OBJEXT) \
+	sp_sctp_num_chunks.$(OBJEXT) sp_sctp_cause_code.$(OBJEXT)
 nodist_libspd_a_OBJECTS = sf_snort_plugin_hdropts.$(OBJEXT)
 libspd_a_OBJECTS = $(am_libspd_a_OBJECTS) $(nodist_libspd_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
@@ -320,7 +327,12 @@ sp_urilen_check.c sp_urilen_check.h \
 sp_file_data.c sp_file_data.h \
 sp_base64_decode.c sp_base64_decode.h \
 sp_base64_data.c sp_base64_data.h \
-sp_pkt_data.c sp_pkt_data.h
+sp_pkt_data.c sp_pkt_data.h \
+sp_sctp_chunk_type.c sp_sctp_chunk_type.h \
+sp_sctp_chunk_flags.c sp_sctp_chunk_flags.h \
+sp_sctp_chunk_field.c sp_sctp_chunk_field.h \
+sp_sctp_num_chunks.c sp_sctp_num_chunks.h \
+sp_sctp_cause_code.c sp_sctp_cause_code.h
 
 copy_files = \
 	if test -f $$dst_file; then \
diff --git a/src/detection-plugins/detection_options.c b/src/detection-plugins/detection_options.c
index d683f2c..f99ed63 100644
--- a/src/detection-plugins/detection_options.c
+++ b/src/detection-plugins/detection_options.c
@@ -69,6 +69,11 @@
 #include "sp_isdataat.h"
 #include "sp_pattern_match.h"
 #include "sp_pcre.h"
+#include "sp_sctp_chunk_type.h"
+#include "sp_sctp_chunk_flags.h"
+#include "sp_sctp_chunk_field.h"
+#include "sp_sctp_num_chunks.h"
+#include "sp_sctp_cause_code.h"
 #ifdef ENABLE_REACT
 #include "sp_react.h"
 #endif
@@ -225,6 +230,21 @@ uint32_t detection_option_hash_func(SFHASHFCN *p, unsigned char *k, int n)
         case RULE_OPTION_TYPE_URILEN:
             hash = UriLenCheckHash(key->option_data);
             break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            hash = SctpChunkTypeHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            hash = SctpChunkFlagsHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            hash = SctpChunkFieldHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            hash = SctpNumChunksHash(key->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
+            hash = SctpCauseCodeHash(key->option_data);
+            break;
 #ifdef DYNAMIC_PLUGIN
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             hash = HdrOptCheckHash(key->option_data);
@@ -379,6 +399,21 @@ int detection_option_key_compare_func(const void *k1, const void *k2, size_t n)
         case RULE_OPTION_TYPE_URILEN:
             ret = UriLenCheckCompare(key1->option_data, key2->option_data);
             break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            ret = SctpChunkTypeCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            ret = SctpChunkFlagsCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            ret = SctpChunkFieldCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            ret = SctpNumChunksCompare(key1->option_data, key2->option_data);
+            break;
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
+            ret = SctpCauseCodeCompare(key1->option_data, key2->option_data);
+            break;
 #ifdef DYNAMIC_PLUGIN
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             ret = HdrOptCheckCompare(key1->option_data, key2->option_data);
@@ -402,130 +437,86 @@ int detection_hash_free_func(void *option_key, void *data)
     switch (key->option_type)
     {
         /* Call free function specific to the key type */
-        case RULE_OPTION_TYPE_ASN1:
-            free(key->option_data);
+        case RULE_OPTION_TYPE_CONTENT:
+        case RULE_OPTION_TYPE_CONTENT_URI:
+            PatternMatchFree(key->option_data);
             break;
+
+        case RULE_OPTION_TYPE_ASN1:
         case RULE_OPTION_TYPE_BYTE_TEST:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_BYTE_JUMP:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_BYTE_EXTRACT:
-            ByteExtractFree(key->option_data);
-            break;
         case RULE_OPTION_TYPE_FLOW:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_CVS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_DSIZE:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_FLOWBIT:
-            FlowBitsFree(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_FTPBOUNCE:
-            /* Data is NULL, nothing to free */
-            break;
         case RULE_OPTION_TYPE_FILE_DATA:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_BASE64_DECODE:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_BASE64_DATA:
-            break;
-        case RULE_OPTION_TYPE_PKT_DATA:
-            break;
         case RULE_OPTION_TYPE_ICMP_CODE:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_ID:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_SEQ:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_ICMP_TYPE:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_FRAGBITS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_FRAG_OFFSET:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_ID:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_OPTION:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IP_PROTO:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_IP_SAME:
-            /* Data is NULL, nothing to free */
-            break;
         case RULE_OPTION_TYPE_IP_TOS:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_IS_DATA_AT:
-            free(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_CONTENT:
-        case RULE_OPTION_TYPE_CONTENT_URI:
-            PatternMatchFree(key->option_data);
-            break;
-        case RULE_OPTION_TYPE_PCRE:
-            PcreFree(key->option_data);
-            break;
-#ifdef ENABLE_REACT
-        case RULE_OPTION_TYPE_REACT:
-            ReactFree(key->option_data);
-            break;
-#endif
 #ifdef ENABLE_RESPOND
         case RULE_OPTION_TYPE_RESPOND:
-            free(key->option_data);
-            break;
 #endif
         case RULE_OPTION_TYPE_RPC_CHECK:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_SESSION:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_ACK:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_FLAG:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_SEQ:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TCP_WIN:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_TTL:
-            free(key->option_data);
-            break;
         case RULE_OPTION_TYPE_URILEN:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+        case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+        case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+        case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
             free(key->option_data);
             break;
+
+        case RULE_OPTION_TYPE_FTPBOUNCE:
+        case RULE_OPTION_TYPE_BASE64_DATA:
+        case RULE_OPTION_TYPE_PKT_DATA:
+        case RULE_OPTION_TYPE_IP_SAME:
+            break;
+
+        case RULE_OPTION_TYPE_BYTE_EXTRACT:
+            ByteExtractFree(key->option_data);
+            break;
+
+        case RULE_OPTION_TYPE_FLOWBIT:
+            FlowBitsFree(key->option_data);
+            break;
+
+        case RULE_OPTION_TYPE_PCRE:
+            PcreFree(key->option_data);
+            break;
+
+#ifdef ENABLE_REACT
+        case RULE_OPTION_TYPE_REACT:
+            ReactFree(key->option_data);
+            break;
+#endif
+
 #ifdef DYNAMIC_PLUGIN
         case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             break;
+
         case RULE_OPTION_TYPE_PREPROCESSOR:
             PreprocessorRuleOptionsFreeFunc(key->option_data);
             break;
+
         case RULE_OPTION_TYPE_DYNAMIC:
             fpDynamicDataFree(key->option_data);
             break;
 #endif
+
         case RULE_OPTION_TYPE_LEAF_NODE:
             break;
     }
@@ -771,12 +762,16 @@ char *option_type_str[] =
     "RULE_OPTION_TYPE_TCP_SEQ",
     "RULE_OPTION_TYPE_TCP_WIN",
     "RULE_OPTION_TYPE_TTL",
-    "RULE_OPTION_TYPE_URILEN"
+    "RULE_OPTION_TYPE_URILEN",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_TYPE",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS",
+    "RULE_OPTION_TYPE_SCTP_CHUNK_FIELD",
+    "RULE_OPTION_TYPE_SCTP_NUM_CHUNKS",
+    "RULE_OPTION_TYPE_SCTP_CAUSE_CODE",
 #ifdef DYNAMIC_PLUGIN
-    ,
     "RULE_OPTION_TYPE_HDR_OPT_CHECK",
     "RULE_OPTION_TYPE_PREPROCESSOR",
-    "RULE_OPTION_TYPE_DYNAMIC"
+    "RULE_OPTION_TYPE_DYNAMIC",
 #endif
 };
 
@@ -1144,6 +1139,11 @@ int detection_option_node_evaluate(detection_option_tree_node_t *node, detection
             case RULE_OPTION_TYPE_TCP_WIN:
             case RULE_OPTION_TYPE_TTL:
             case RULE_OPTION_TYPE_URILEN:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_TYPE:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS:
+            case RULE_OPTION_TYPE_SCTP_CHUNK_FIELD:
+            case RULE_OPTION_TYPE_SCTP_NUM_CHUNKS:
+            case RULE_OPTION_TYPE_SCTP_CAUSE_CODE:
 #ifdef DYNAMIC_PLUGIN
             case RULE_OPTION_TYPE_HDR_OPT_CHECK:
             case RULE_OPTION_TYPE_PREPROCESSOR:
diff --git a/src/detection-plugins/sp_sctp_cause_code.c b/src/detection-plugins/sp_sctp_cause_code.c
new file mode 100644
index 0000000..9d7a5ed
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_cause_code.c
@@ -0,0 +1,324 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "sp_sctp_cause_code.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpCauseCodePerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/**
+ * SctpCauseCodeHash - Hash function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpCauseCodeHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpCauseCodeData *sccd = (SctpCauseCodeData *)data;
+
+    a = sccd->cause_code;
+    b = RULE_OPTION_TYPE_SCTP_CAUSE_CODE;
+    c = 0;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpCauseCodeCompare - Comparator function for sctp_cause_code.
+ * @l: pointer to SctpCauseCodeData (left side).
+ * @r: pointer to SctpCauseCodeData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpCauseCodeCompare(void *l, void *r)
+{
+    SctpCauseCodeData *left = (SctpCauseCodeData *)l;
+    SctpCauseCodeData *right = (SctpCauseCodeData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->cause_code != right->cause_code)
+        return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpCauseCodeSetup - Setup function for sctp_cause_code.
+ *
+ * Links the 'sctp_cause_code' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpCauseCodeSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_cause_code", SctpCauseCodeInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_cause_code", &sctpCauseCodePerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpCauseCode Initialized\n"););
+}
+
+/**
+ * SctpCauseCodeInit - Initialization function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpCauseCodeInit(char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpCauseCodeData *sccd;
+    void *sccd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_cause_code' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* This keyword requires 'sctp_chunk_type' to be used first. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] == NULL)
+        FatalError("%s(%d): 'sctp_chunk_type' must be used before "
+                   "'sctp_cause_code'.", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CAUSE_CODE])
+        FatalError("%s(%d): 'sctp_cause_code' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CAUSE_CODE] = (SctpCauseCodeData *)
+            SnortAlloc(sizeof(SctpCauseCodeData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpCauseCodeParse(data, otn);
+    sccd = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(RULE_OPTION_TYPE_SCTP_CAUSE_CODE,
+                             (void *)sccd, &sccd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CAUSE_CODE] = (SctpCauseCodeData *)sccd_dup;
+        free(sccd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpCauseCodeEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CAUSE_CODE;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+}
+
+/**
+ * SctpCauseCodeParse - Parsing function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpCauseCodeData
+ * structure.  Returns void.
+ */
+void SctpCauseCodeParse(char *data, OptTreeNode *otn)
+{
+    char *end;
+    uint16_t val = 0;
+    SctpChunkTypeData *sctd;
+    SctpCauseCodeData *sccd;
+
+    /* Get both the chunk type and cause code data sets. */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+    sccd = otn->ds_list[PLUGIN_SCTP_CAUSE_CODE];
+
+    if ((sctd->parse_count == 1) || (sctd->parse_count == 2))
+    {
+        if (sctd->abort || sctd->error)
+        {
+            val = strtoul(data, &end, 10);
+            if ((val >= 0) && (val <= UINT16_MAX))
+                sccd->cause_code = (uint16_t)val;
+            else
+                FatalError("Error at %s:%d: Invalid 'sctp_num_chunks' "
+                           "argument.\n", file_name, file_line);
+        }
+        else
+            FatalError("Error at %s:%d: When using 'sctp_cause_code', the "
+                       "'sctp_chunk_type' keyword can only contain 'ABORT' "
+                       "or 'ERROR'.\n", file_name, file_line);
+    }
+    else
+        FatalError("Error at %s:%d: The number of specified chunks to "
+                   "'sctp_chunk_type' exceeds the number of chunk types in "
+                   "SCTP that support cause codes.\n", file_name, file_line);
+
+    return;
+}
+
+/**
+ * SctpCauseCodeEval - Evaluation function for sctp_cause_code.
+ * @data: pointer to SctpCauseCodeData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpCauseCodeEval(void *data, Packet *p)
+{
+    SctpCauseCodeData *sccd = (SctpCauseCodeData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || !sccd)
+        return rval;
+
+    PREPROC_PROFILE_START(sctpCauseCodePerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Loop through the chunks in the packet and for each DATA chunk, check
+     * check the pp_id field against the supplied value for a match.
+     */
+    while ((c != NULL) && 
+           (remaining_len != 0) && 
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        uint16_t remaining_chunk_len = chunk_length;
+		SctpCause *cause = NULL;
+        remaining_len -= chunk_length;
+        
+        /*
+         * Although we have already validated which chunks we want to check
+         * via 'sctp_chunk_type', we still have to check the chunk types
+         * here because of chunk bundling.
+         */
+        switch (c->type)
+        {
+            case SCTP_ABORT_C:
+                cause = (SctpCause *)c->abort.causes;
+                remaining_chunk_len -= SCTP_ABORT_C_LEN;
+                break;
+
+            case SCTP_ERROR_C:
+                cause = (SctpCause *)c->error.causes;
+                remaining_chunk_len -= SCTP_ERROR_C_LEN;
+                break;
+
+            case SCTP_DATA_C:
+            case SCTP_INIT_C:
+            case SCTP_INIT_ACK_C:
+            case SCTP_SACK_C:
+            case SCTP_HEARTBEAT_C:
+            case SCTP_HEARTBEAT_ACK_C:
+            case SCTP_SHUTDOWN_C:
+            case SCTP_SHUTDOWN_ACK_C:
+            case SCTP_COOKIE_ECHO_C:
+            case SCTP_COOKIE_ACK_C:
+            case SCTP_ECNE_C:
+            case SCTP_CWR_C:
+            case SCTP_SHUTDOWN_COMPLETE_C:
+            case SCTP_AUTH_C:
+            case SCTP_ASCONF_ACK_C:
+            case SCTP_PKTDROP_C:
+            case SCTP_PAD_C:
+            case SCTP_FORWARD_TSN_C:
+            case SCTP_ASCONF_C:
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_cause_code): Encountered "
+                                  "an unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        /*
+         * If we encountered either an ABORT chunk or ERROR chunk, then
+         * cause_data will not be NULL and the below loop, which mimics the
+         * chunk-counting loop in src/decode.c::DecodeSCTP(), will walk the
+         * causes and look for a matching cause code.
+         *
+         * Note that ABORT can have zero or more causes and ERROR will have
+         * at least one cause.
+         */
+        while ((cause != NULL) &&
+               (remaining_chunk_len != 0) &&
+               (rval == DETECTION_OPTION_NO_MATCH))
+        {
+            uint16_t cause_len = SCTP_ADD_PADDING(ntohs(cause->length));
+            remaining_chunk_len -= cause_len;
+            
+            if (ntohs(cause->code) == sccd->cause_code)
+                rval = DETECTION_OPTION_MATCH;
+
+            remaining_chunk_len = SctpGetNextCause(c, remaining_chunk_len,
+                                                   cause_len);
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpCauseCodePerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_cause_code.h b/src/detection-plugins/sp_sctp_cause_code.h
new file mode 100644
index 0000000..46e4b38
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_cause_code.h
@@ -0,0 +1,38 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CAUSE_CODE_H__
+#define __SP_SCTP_CAUSE_CODE_H__
+
+/* SctpCauseCode data structure. */
+typedef struct _SctpCauseCodeData
+{
+    uint16_t cause_code;
+} SctpCauseCodeData;
+
+/* Prototypes. */
+uint32_t SctpCauseCodeHash(void *data);
+int SctpCauseCodeCompare(void *l, void *r);
+void SctpCauseCodeSetup(void);
+void SctpCauseCodeInit(char *data, OptTreeNode *otn, int protocol);
+void SctpCauseCodeParse(char *data, OptTreeNode *otn);
+int SctpCauseCodeEval(void *data, Packet *p);
+
+#endif  /* __SP_SCTP_CAUSE_CODE_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_field.c b/src/detection-plugins/sp_sctp_chunk_field.c
new file mode 100644
index 0000000..bbc99cb
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_field.c
@@ -0,0 +1,635 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "sp_sctp_chunk_field.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkFieldPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/**
+ * SctpChunkFieldHash - Hash function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkFieldHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkFieldData *scfd = (SctpChunkFieldData *)data;
+
+    a = scfd->field;
+    b = scfd->operator;
+    c = scfd->value_min;
+    mix(a, b, c);
+    a += scfd->value_max;
+    b += RULE_OPTION_TYPE_SCTP_CHUNK_FIELD;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkFieldCompare - Comparator function for sctp_chunk_field.
+ * @l: pointer to SctpChunkFieldData (left side).
+ * @r: pointer to SctpChunkFieldData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkFieldCompare(void *l, void *r)
+{
+    SctpChunkFieldData *left = (SctpChunkFieldData *)l;
+    SctpChunkFieldData *right = (SctpChunkFieldData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->field != right->field)         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->operator != right->operator)   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->value_min != right->value_min) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->value_max != right->value_max) return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkFieldSetup - Setup function for sctp_chunk_field.
+ *
+ * Links the 'sctp_chunk_field' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkFieldSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_field", SctpChunkFieldInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_field", &sctpChunkFieldPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkField Initialized\n"););
+}
+
+/**
+ * SctpChunkFieldInit - Initialization function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkFieldInit(char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkFieldData *scfd;
+    void *scfd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_field' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    scfd = (SctpChunkFieldData *)SnortAlloc(sizeof(SctpChunkFieldData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkFieldParse(data, scfd, otn);
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(RULE_OPTION_TYPE_SCTP_CHUNK_FIELD,
+                             (void *)scfd, &scfd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        free(scfd);
+        scfd = (SctpChunkFieldData *)scfd_dup;
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkFieldEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_FIELD;
+    fpl->context = (void *)scfd;
+}
+
+/**
+ * SctpChunkFieldParse - Parsing function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkFieldData
+ * structure.  Returns void.
+ */
+void SctpChunkFieldParse(char *data, SctpChunkFieldData *scfd, OptTreeNode *otn)
+{
+    char *str, *strtmp, *tok, *end;
+    char *chunk, *field;
+    uint32_t parse_status = SCTP_CHUNK_FIELD_PARSE_NONE;
+
+    /* Covert to a string and make a copy of the pointer. */
+    str = SnortStrdup(data);
+    strtmp = str;
+
+    /* Skip any leading whitespace. */
+    while(isspace((int)*strtmp)) strtmp++;
+
+    /* Parse. */
+    tok = strtok(strtmp, ",");
+    while (tok)
+    {
+        /* Continue to skip any whitespace. */
+        while(isspace((int)*tok)) tok++;
+
+        /* Parse the first field, which should be the chunk type. */
+        if (parse_status == SCTP_CHUNK_FIELD_PARSE_NONE)
+        {
+            /*
+             * Parse the first operand: the chunk type.  This is left-shifted
+             * by 8 to store it in the upper-half of the scfd->field variable.
+             * The lower 8 bits will be used for storing the particular field
+             * ID that we wish to check.
+             */
+            if      (!strcasecmp("DATA", tok))              scfd->field = (SCTP_DATA_C << 8);
+            else if (!strcasecmp("INIT", tok))              scfd->field = (SCTP_INIT_C << 8);
+            else if (!strcasecmp("INIT-ACK", tok))          scfd->field = (SCTP_INIT_ACK_C << 8);
+            else if (!strcasecmp("SACK", tok))              scfd->field = (SCTP_SACK_C << 8);
+            else if (!strcasecmp("HEARTBEAT", tok))         scfd->field = (SCTP_HEARTBEAT_C << 8);
+            else if (!strcasecmp("HEARTBEAT-ACK", tok))     scfd->field = (SCTP_HEARTBEAT_ACK_C << 8);
+            else if (!strcasecmp("ABORT", tok))             scfd->field = (SCTP_ABORT_C << 8);
+            else if (!strcasecmp("SHUTDOWN", tok))          scfd->field = (SCTP_SHUTDOWN_C << 8);
+            else if (!strcasecmp("SHUTDOWN-ACK", tok))      scfd->field = (SCTP_SHUTDOWN_ACK_C << 8);
+            else if (!strcasecmp("ERROR", tok))             scfd->field = (SCTP_ERROR_C << 8);
+            else if (!strcasecmp("COOKIE-ECHO", tok))       scfd->field = (SCTP_COOKIE_ECHO_C << 8);
+            else if (!strcasecmp("COOKIE-ACK", tok))        scfd->field = (SCTP_COOKIE_ACK_C << 8);
+            else if (!strcasecmp("ECNE", tok))              scfd->field = (SCTP_ECNE_C << 8);
+            else if (!strcasecmp("CWR", tok))               scfd->field = (SCTP_CWR_C << 8);
+            else if (!strcasecmp("SHUTDOWN-COMPLETE", tok)) scfd->field = (SCTP_SHUTDOWN_COMPLETE_C << 8);
+            else if (!strcasecmp("AUTH", tok))              scfd->field = (SCTP_AUTH_C << 8);
+            else if (!strcasecmp("ASCONF-ACK", tok))        scfd->field = (SCTP_ASCONF_ACK_C << 8);
+            else if (!strcasecmp("PKTDROP", tok))           scfd->field = (SCTP_PKTDROP_C << 8);
+            else if (!strcasecmp("RE-CONFIG", tok))         scfd->field = (SCTP_RECONFIG_C << 8);
+            else if (!strcasecmp("PAD", tok))               scfd->field = (SCTP_PAD_C << 8);
+            else if (!strcasecmp("FORWARD-TSN", tok))       scfd->field = (SCTP_FORWARD_TSN_C << 8);
+            else if (!strcasecmp("ASCONF", tok))            scfd->field = (SCTP_ASCONF_C << 8);
+            else
+                FatalError("Error at %s:%d: %s is not a recognized argument to "
+                           "'sctp_chunk_field'.\n", file_name, file_line, tok);
+
+            /*
+             * Now check the scfd->field value for chunks that don't have any
+             * numeric fields to check (yet).  We do it this way because one day,
+             * they may get a numeric field of some kind.
+             */
+            switch (scfd->field >> 8)
+            {
+                case SCTP_HEARTBEAT_C:
+                case SCTP_HEARTBEAT_ACK_C:
+                case SCTP_ABORT_C:
+                case SCTP_SHUTDOWN_ACK_C:
+                case SCTP_ERROR_C:
+                case SCTP_COOKIE_ECHO_C:
+                case SCTP_COOKIE_ACK_C:
+                case SCTP_SHUTDOWN_COMPLETE_C:
+                case SCTP_RECONFIG_C:
+                case SCTP_PAD_C:
+                    FatalError("Error at %s:%d: %s does not have any numeric "
+                               "fields to check for.", file_name, file_line, tok);
+                    break;
+            }
+            
+            parse_status = SCTP_CHUNK_FIELD_PARSE_TYPE;
+            chunk = tok;
+            tok = strtok(NULL, ",");
+        }
+        
+        /*
+         * If the type is parsed, move on to parse the particular field to
+         * check in the selected chunk type.
+         */
+        else if (parse_status == SCTP_CHUNK_FIELD_PARSE_TYPE)
+        {
+            switch (scfd->field >> 8)
+            {
+                case SCTP_DATA_C:
+                    if      (!strcasecmp("tsn", tok))             scfd->field += SCTP_DATA_TSN;
+                    else if (!strcasecmp("stream_id", tok))       scfd->field += SCTP_DATA_STREAM_ID;
+                    else if (!strcasecmp("stream_seq", tok))      scfd->field += SCTP_DATA_STREAM_SEQ;
+                    else if (!strcasecmp("payload_id", tok))      scfd->field += SCTP_DATA_PAYLOAD_ID;
+                    else goto field_error;
+                    break;
+
+                case SCTP_INIT_C:
+                    if      (!strcasecmp("initiate_tag", tok))    scfd->field += SCTP_INIT_INITIATE_TAG;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_INIT_A_RWND;
+                    else if (!strcasecmp("num_out_streams", tok)) scfd->field += SCTP_INIT_NUM_OUT_STRMS;
+                    else if (!strcasecmp("num_in_streams", tok))  scfd->field += SCTP_INIT_NUM_IN_STRMS;
+                    else if (!strcasecmp("tsn", tok))             scfd->field += SCTP_INIT_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_INIT_ACK_C:
+                    if      (!strcasecmp("initiate_tag", tok))    scfd->field += SCTP_INIT_ACK_INITIATE_TAG;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_INIT_ACK_A_RWND;
+                    else if (!strcasecmp("num_out_streams", tok)) scfd->field += SCTP_INIT_ACK_NUM_OUT_STRMS;
+                    else if (!strcasecmp("num_in_streams", tok))  scfd->field += SCTP_INIT_ACK_NUM_IN_STRMS;
+                    else if (!strcasecmp("tsn", tok))             scfd->field += SCTP_INIT_ACK_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_SACK_C:
+                    if      (!strcasecmp("cuml_tsn_ack", tok))    scfd->field += SCTP_SACK_CUML_TSN_ACK;
+                    else if (!strcasecmp("a_rwnd", tok))          scfd->field += SCTP_SACK_A_RWND;
+                    else if (!strcasecmp("num_gap_acks", tok))    scfd->field += SCTP_SACK_NUM_GAP_ACKS;
+                    else if (!strcasecmp("num_dupe_tsns", tok))   scfd->field += SCTP_SACK_NUM_DUPE_TSNS;
+                    else goto field_error;
+                    break;
+
+                case SCTP_SHUTDOWN_C:
+                    if      (!strcasecmp("tsn_ack", tok))         scfd->field += SCTP_SHUTDOWN_TSN_ACK;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ECNE_C:
+                    if      (!strcasecmp("lowest_tsn", tok))      scfd->field += SCTP_ECNE_LOWEST_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_CWR_C:
+                    if      (!strcasecmp("lowest_tsn", tok))      scfd->field += SCTP_CWR_LOWEST_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_AUTH_C:
+                    if      (!strcasecmp("skey_id", tok))         scfd->field += SCTP_AUTH_SKEY_ID;
+                    else if (!strcasecmp("hmac_id", tok))         scfd->field += SCTP_AUTH_HMAC_ID;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ASCONF_ACK_C:
+                    if      (!strcasecmp("seq", tok))             scfd->field += SCTP_ASCONF_ACK_SEQ;
+                    else goto field_error;
+                    break;
+
+                case SCTP_PKTDROP_C:
+                    if      (!strcasecmp("lbmr", tok))            scfd->field += SCTP_PKTDROP_LBMR;
+                    else if (!strcasecmp("queue_size", tok))      scfd->field += SCTP_PKTDROP_QUEUE_SIZE;
+                    else if (!strcasecmp("trunc_length", tok))    scfd->field += SCTP_PKTDROP_TRUNC_LENGTH;
+                    else if (!strcasecmp("reserved", tok))        scfd->field += SCTP_PKTDROP_RESERVED;
+                    else goto field_error;
+                    break;
+
+                case SCTP_FORWARD_TSN_C:
+                    if      (!strcasecmp("new_tsn", tok))         scfd->field += SCTP_FORWARD_TSN_NEW_TSN;
+                    else goto field_error;
+                    break;
+
+                case SCTP_ASCONF_C:
+                    if      (!strcasecmp("seq", tok))             scfd->field += SCTP_ASCONF_SEQ;
+                    else goto field_error;
+                    break;
+
+                default:
+                    FatalError("Error at %s:%d: Unknown chunk type 0x%2x or "
+                               "chunk does not have any numeric fields to "
+                               "check for.", file_name, file_line,
+                               (scfd->field >> 8));
+                    break;
+            }
+
+            parse_status = SCTP_CHUNK_FIELD_PARSE_FIELD;
+            field = tok;
+            tok = strtok(NULL, ",");
+        }
+
+        /*
+         * If the field is parsed, move on to parse the value.  The value can
+         * be a static value, or a ranged value (<, >, and <>).
+         */
+        else if (parse_status == SCTP_CHUNK_FIELD_PARSE_FIELD)
+        {
+            UNumericRange rng;
+            char *toktmp = tok;
+            uint32_t num = 0;
+            int rval = -3;
+
+            /* Parse out the operands of the range. */
+            rval = SnortStrToUNumRng(toktmp, "<>", &rng);
+            if (rval == SNORT_STR2RNG_SUCCESS)
+            {
+                scfd->value_min = rng.min;
+                scfd->value_max = rng.max;
+                scfd->operator = SCTP_CHUNK_FIELD_OP_RANGE;
+            }
+
+            /* Not a ranged value. */
+            else if (rval == SNORT_STR2RNG_NOTRNG)
+            {
+                if (*toktmp == '>')
+                {
+                    toktmp++;
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_GT;
+                }
+                else if(*toktmp == '<')
+                {
+                    toktmp++;
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_LT;
+                }
+                else
+                    scfd->operator = SCTP_CHUNK_FIELD_OP_EQUAL;
+            }
+
+            /* Error? */
+            else if (rval == SNORT_STR2RNG_ERROR)
+                goto value_error;
+
+            /* Parse the remainder and store it as the minimum value. */
+            if (scfd->operator != SCTP_CHUNK_FIELD_OP_RANGE)
+            {
+                num = strtoul(toktmp, &end, 10);
+                if (*end)
+                    goto value_error;
+
+                scfd->value_min = num;
+            }
+
+            parse_status = SCTP_CHUNK_FIELD_PARSE_VALUE;
+            tok = NULL;
+        }
+
+    }
+
+    return;
+
+field_error:
+    FatalError("Error %s:%d: The %s chunk does not have a field by the "
+                "name of %s!\n", file_name, file_line, chunk, tok);
+
+    return;
+    
+value_error:
+    FatalError("Error at %s:%d: Invalid value passed to "
+               "'sctp_chunk_field'!\n", file_name, file_line);
+
+    return;
+}
+
+/**
+ * SctpChunkFieldEval - Evaluation function for sctp_chunk_field.
+ * @data: pointer to SctpChunkFieldData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkFieldEval(void *data, Packet *p)
+{
+    SctpChunkFieldData *scfd = (SctpChunkFieldData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || !scfd)
+        return rval;
+
+    PREPROC_PROFILE_START(sctpChunkFieldPerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+    uint32_t chunk_type = (scfd->field >> 8);
+    uint32_t field = (scfd->field & 0x00ff);
+    uint32_t val = 0;
+
+    /*
+     * Loop through the chunks in the packet and for each chunk, check
+     * check the specified field against the supplied value(s) for a match.
+     */
+    while ((c != NULL) &&
+           (remaining_len != 0) &&
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        /*
+         * Continue the while loop if the chunk type in the packet does
+         * not match the chunk type selected by the rule keyword.
+         */
+        if (c->type != chunk_type)
+            goto next_chunk;
+
+        /* Fetch the specific field value from the specified chunk. */
+        switch (c->type)
+        {
+            case SCTP_DATA_C:
+                switch (field)
+                {
+                    case SCTP_DATA_TSN:               val = ntohl(c->data.tsn);                 break;
+                    case SCTP_DATA_STREAM_ID:         val = ntohs(c->data.stream_id);           break;
+                    case SCTP_DATA_STREAM_SEQ:        val = ntohs(c->data.stream_seq);          break;
+                    case SCTP_DATA_PAYLOAD_ID:        val = ntohl(c->data.payload_id);          break;
+                }
+                break;
+                
+            case SCTP_INIT_C:
+                switch (field)
+                {
+                    case SCTP_INIT_INITIATE_TAG:      val = ntohl(c->init.initiate_tag);        break;
+                    case SCTP_INIT_A_RWND:            val = ntohl(c->init.a_rwnd);              break;
+                    case SCTP_INIT_NUM_OUT_STRMS:     val = ntohs(c->init.num_out_streams);     break;
+                    case SCTP_INIT_NUM_IN_STRMS:      val = ntohs(c->init.num_in_streams);      break;
+                    case SCTP_INIT_TSN:               val = ntohl(c->init.tsn);                 break;
+                }
+                break;
+
+            case SCTP_INIT_ACK_C:
+                switch (field)
+                {
+                    case SCTP_INIT_ACK_INITIATE_TAG:  val = ntohl(c->init_ack.initiate_tag);    break;
+                    case SCTP_INIT_ACK_A_RWND:        val = ntohl(c->init_ack.a_rwnd);          break;
+                    case SCTP_INIT_ACK_NUM_OUT_STRMS: val = ntohs(c->init_ack.num_out_streams); break;
+                    case SCTP_INIT_ACK_NUM_IN_STRMS:  val = ntohs(c->init_ack.num_in_streams);  break;
+                    case SCTP_INIT_ACK_TSN:           val = ntohl(c->init_ack.tsn);             break;
+                }
+                break;
+
+            case SCTP_SACK_C:
+                switch (field)
+                {
+                    case SCTP_SACK_CUML_TSN_ACK:      val = ntohl(c->sack.cuml_tsn_ack);        break;
+                    case SCTP_SACK_A_RWND:            val = ntohl(c->sack.a_rwnd);              break;
+                    case SCTP_SACK_NUM_GAP_ACKS:      val = ntohs(c->sack.num_gap_acks);        break;
+                    case SCTP_SACK_NUM_DUPE_TSNS:     val = ntohs(c->sack.num_dupe_tsns);       break;
+                }
+                break;
+            
+            case SCTP_SHUTDOWN_C:
+                switch (field)
+                {
+                    case SCTP_SHUTDOWN_TSN_ACK:       val = ntohl(c->shutdown.tsn_ack);         break;
+                }
+                break;
+
+            case SCTP_ECNE_C:
+                switch(field)
+                {
+                    case SCTP_ECNE_LOWEST_TSN:        val = ntohl(c->ecne.lowest_tsn);          break;
+                }
+                break;
+
+            case SCTP_CWR_C:
+                switch (field)
+                {
+                    case SCTP_CWR_LOWEST_TSN:         val = ntohl(c->cwr.lowest_tsn);           break;
+                }
+                break;
+
+            case SCTP_AUTH_C:
+                switch (field)
+                {
+                    case SCTP_AUTH_SKEY_ID:           val = ntohs(c->auth.skey_id);             break;
+                    case SCTP_AUTH_HMAC_ID:           val = ntohs(c->auth.skey_id);             break;
+                }
+                break;
+
+            case SCTP_ASCONF_ACK_C:
+                switch (field)
+                {
+                    case SCTP_ASCONF_ACK_SEQ:         val = ntohl(c->asconf_ack.seq);           break;
+                }
+                break;
+
+            case SCTP_PKTDROP_C:
+                switch (field)
+                {
+                    case SCTP_PKTDROP_LBMR:           ntohl(val = c->pktdrop.lbmr);             break;
+                    case SCTP_PKTDROP_QUEUE_SIZE:     ntohl(val = c->pktdrop.queue_size);       break;
+                    case SCTP_PKTDROP_TRUNC_LENGTH:   ntohs(val = c->pktdrop.trunc_length);     break;
+                    case SCTP_PKTDROP_RESERVED:       ntohs(val = c->pktdrop.reserved);         break;
+                }
+                break;
+
+            case SCTP_FORWARD_TSN_C:
+                switch (field)
+                {
+                    case SCTP_FORWARD_TSN_NEW_TSN:    val = ntohl(c->forward_tsn.new_tsn);      break;
+                }
+                break;
+
+            case SCTP_ASCONF_C:
+                switch (field)
+                {
+                    case SCTP_ASCONF_SEQ:             val = ntohl(c->asconf.seq);               break;
+                }
+                break;
+
+            case SCTP_HEARTBEAT_C:
+            case SCTP_HEARTBEAT_ACK_C:
+            case SCTP_ABORT_C:
+            case SCTP_SHUTDOWN_ACK_C:
+            case SCTP_ERROR_C:
+            case SCTP_COOKIE_ECHO_C:
+            case SCTP_COOKIE_ACK_C:
+            case SCTP_SHUTDOWN_COMPLETE_C:
+            case SCTP_RECONFIG_C:
+            case SCTP_PAD_C:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_field): Chunk Type 0x%2x "
+                                  "does not have any numeric fields to compare "
+                                  "against.\n", c->type));
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_field): Encountered "
+                                  "an unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        /*
+         * Now compare the value from the field against the user-specified
+         * value, based on the set operator.
+         */
+        switch (scfd->operator)
+        {
+            case SCTP_CHUNK_FIELD_OP_EQUAL:
+                if (scfd->value_min == val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_LT:
+                if (scfd->value_min > val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_GT:
+                if (scfd->value_min < val)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_CHUNK_FIELD_OP_RANGE:
+                if ((scfd->value_min <= val) &&
+                    (scfd->value_max >= val))
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            default:
+                break;
+        }
+        break;
+
+next_chunk:
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkFieldPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_field.h b/src/detection-plugins/sp_sctp_chunk_field.h
new file mode 100644
index 0000000..70c01b9
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_field.h
@@ -0,0 +1,121 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_FIELD_H__
+#define __SP_SCTP_CHUNK_FIELD_H__
+
+/* These are used to know what phase of the parsing that we are on. */
+#define SCTP_CHUNK_FIELD_PARSE_NONE         0x00
+#define SCTP_CHUNK_FIELD_PARSE_TYPE         0x01
+#define SCTP_CHUNK_FIELD_PARSE_FIELD        0x02
+#define SCTP_CHUNK_FIELD_PARSE_VALUE        0x03
+
+/* Operator definitions for sctp_payload_id. */
+#define SCTP_CHUNK_FIELD_OP_EQUAL           0x01
+#define SCTP_CHUNK_FIELD_OP_LT              0x02
+#define SCTP_CHUNK_FIELD_OP_GT              0x03
+#define SCTP_CHUNK_FIELD_OP_RANGE           0x04
+
+/* Field definitions for DATA. */
+#define SCTP_DATA_TSN                       0x01
+#define SCTP_DATA_STREAM_ID                 0x02
+#define SCTP_DATA_STREAM_SEQ                0x03
+#define SCTP_DATA_PAYLOAD_ID                0x04
+
+/* Field definitions for INIT. */
+#define SCTP_INIT_INITIATE_TAG              0x01
+#define SCTP_INIT_A_RWND                    0x02
+#define SCTP_INIT_NUM_OUT_STRMS             0x03
+#define SCTP_INIT_NUM_IN_STRMS              0x04
+#define SCTP_INIT_TSN                       0x05
+
+/* Field definitions for INIT-ACK. */
+#define SCTP_INIT_ACK_INITIATE_TAG          0x01
+#define SCTP_INIT_ACK_A_RWND                0x02
+#define SCTP_INIT_ACK_NUM_OUT_STRMS         0x03
+#define SCTP_INIT_ACK_NUM_IN_STRMS          0x04
+#define SCTP_INIT_ACK_TSN                   0x05
+
+/* Field definitions for SACK. */
+#define SCTP_SACK_CUML_TSN_ACK              0x01
+#define SCTP_SACK_A_RWND                    0x02
+#define SCTP_SACK_NUM_GAP_ACKS              0x03
+#define SCTP_SACK_NUM_DUPE_TSNS             0x04
+
+/* No field definitions for HEARTBEAT. */
+/* No field definitions for HEARTBEAT-ACK. */
+/* No field definitions for ABORT. */
+
+/* Field definitions for SHUTDOWN. */
+#define SCTP_SHUTDOWN_TSN_ACK               0x01
+
+/* No field definitions for SHUTDOWN-ACK. */
+/* No field definitions for ERROR. */
+/* No field definitions for COOKIE-ECHO. */
+/* No field definitions for COOKIE-ACK. */
+
+/* Field definitions for ECNE. */
+#define SCTP_ECNE_LOWEST_TSN                0x01
+
+/* Field definitions for CWR. */
+#define SCTP_CWR_LOWEST_TSN                 0x01
+
+/* No field definitions for SHUTDOWN-COMPLETE. */
+
+/* Field definitions for AUTH. */
+#define SCTP_AUTH_SKEY_ID                   0x01
+#define SCTP_AUTH_HMAC_ID                   0x02
+
+/* Field definitions for ASCONF-ACK. */
+#define SCTP_ASCONF_ACK_SEQ                 0x01
+
+/* Field definitions for PKTDROP. */
+#define SCTP_PKTDROP_LBMR                   0x01
+#define SCTP_PKTDROP_QUEUE_SIZE             0x02
+#define SCTP_PKTDROP_TRUNC_LENGTH           0x03
+#define SCTP_PKTDROP_RESERVED               0x04
+
+/* No field definitions for RE-CONFIG. */
+/* No field definitions for PAD. */
+
+/* Field definitions for FORWARD-TSN. */
+#define SCTP_FORWARD_TSN_NEW_TSN            0x01
+
+/* Field definitions for ASCONF. */
+#define SCTP_ASCONF_SEQ                     0x01
+
+/* SctpChunkField data structure. */
+typedef struct _SctpChunkFieldData
+{
+    uint16_t field;
+    uint8_t operator;
+    uint32_t value_min;
+    uint32_t value_max;
+} SctpChunkFieldData;
+
+/* Prototypes. */
+uint32_t SctpChunkFieldHash(void *data);
+int SctpChunkFieldCompare(void *l, void *r);
+void SctpChunkFieldSetup(void);
+void SctpChunkFieldInit(char *data, OptTreeNode *otn, int protocol);
+void SctpChunkFieldParse(char *data, SctpChunkFieldData *scfd, OptTreeNode *otn);
+int SctpChunkFieldEval(void *data, Packet *p);
+
+#endif  /* __SP_SCTP_CHUNK_FIELD_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_flags.c b/src/detection-plugins/sp_sctp_chunk_flags.c
new file mode 100644
index 0000000..4e7f57f
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_flags.c
@@ -0,0 +1,464 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "sp_sctp_chunk_flags.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkFlagsPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/**
+ * SctpChunkFlagsHash - Hash function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkFlagsHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkFlagsData *scfd = (SctpChunkFlagsData *)data;
+
+    a = scfd->mode;
+    b = scfd->flags || (scfd->mask << 8);
+    c = RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkFlagsCompare - Comparator function for sctp_chunk_flags.
+ * @l: pointer to SctpChunkFlagsData (left side).
+ * @r: pointer to SctpChunkFlagsData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkFlagsCompare(void *l, void *r)
+{
+    SctpChunkFlagsData *left = (SctpChunkFlagsData *)l;
+    SctpChunkFlagsData *right = (SctpChunkFlagsData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->mode != right->mode)   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->flags != right->flags) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->mask != right->mask)   return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkFlagsSetup - Setup function for sctp_chunk_flags.
+ *
+ * Links the 'sctp_chunk_flags' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkFlagsSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_flags", SctpChunkFlagsInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_flags", &sctpChunkFlagsPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkFlags Initialized\n"););
+}
+
+/**
+ * SctpChunkFlagsInit - Initialization function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkFlagsInit(char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkFlagsData *scfd;
+    void *scfd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_flags' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* This keyword requires 'sctp_chunk_type' to be used first. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] == NULL)
+        FatalError("%s(%d): 'sctp_chunk_type' must be used before "
+                   "'sctp_chunk_flags'.", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS])
+        FatalError("%s(%d): 'sctp_chunk_flags' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS] = (SctpChunkFlagsData *)
+            SnortAlloc(sizeof(SctpChunkFlagsData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkFlagsParse(data, otn);
+    scfd = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS,
+                             (void *)scfd, &scfd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS] = (SctpChunkFlagsData *)scfd_dup;
+        free(scfd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkFlagsEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+}
+
+/**
+ * SctpChunkFlagsParse - Parsing function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkFlagsData
+ * structure.  Returns void.
+ */
+void SctpChunkFlagsParse(char *data, OptTreeNode *otn)
+{
+    char *params, *end = NULL;
+    uint32_t comma_set = 0;
+    SctpChunkTypeData *sctd;
+    SctpChunkFlagsData *scfd;
+
+    /* Get both the chunk flags and chunk type data sets. */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+    scfd = otn->ds_list[PLUGIN_SCTP_CHUNK_FLAGS];
+
+    params = SnortStrdup(data);
+    while (isspace((u_char)*params)) params++;     /* nuke leading whitespace */
+    if (strlen(params) == 0)
+        FatalError("Error at %s:%d: Missing argument to the 'sctp_chunk_flags' "
+                   "keyword\n", file_name, file_line);
+
+    /* Parse each flag bit.  Loop once to validate, then again to parse. */
+    if (sctd->parse_count == 1)
+    {
+        end = params + strlen(params);
+
+        while ((params < end) && (comma_set == 0))
+        {
+            /* Parse operators or the '0' bit. */
+            switch (*params)
+            {
+            case '+':
+                scfd->mode = SCTP_FLAGS_ALL;
+                params++;
+                break;
+
+            case '*':
+                scfd->mode = SCTP_FLAGS_ANY;
+                params++;
+                break;
+
+            case '!':
+                scfd->mode = SCTP_FLAGS_NOT;
+                params++;
+                break;
+
+            case '0':
+                scfd->flags = 0;
+                params++;
+                continue;
+                break;
+
+            case ',':
+                comma_set = 1;
+                params++;
+                continue;
+                break;
+            }
+
+            /* Parse - Certain flag bits only work with certain chunk types. */
+            if (sctd->data)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('i', 'I', flags, DATA, I);
+                    SCTP_CHECK_CHUNK_FLAGS('u', 'U', flags, DATA, U);
+                    SCTP_CHECK_CHUNK_FLAGS('b', 'B', flags, DATA, B);
+                    SCTP_CHECK_CHUNK_FLAGS('e', 'E', flags, DATA, E);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for DATA chunks "
+                               "are IUBE.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->abort)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, ABORT, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for ABORT chunks "
+                               "are T.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->shutdown_complete)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, SHUTDOWN_COMPLETE, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for "
+                               "SHUTDOWN_COMPLETE chunks are T.\n",
+                               file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->pktdrop)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('c', 'C', flags, PKTDROP, C);
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', flags, PKTDROP, T);
+                    SCTP_CHECK_CHUNK_FLAGS('b', 'B', flags, PKTDROP, B);
+                    SCTP_CHECK_CHUNK_FLAGS('m', 'M', flags, PKTDROP, M);
+
+                default:
+                    FatalError("Error at %s:%d: Valid flags for PKTDROP chunks "
+                               "are CTBM.\n", file_name, file_line);
+                    break;
+                }
+            else
+                FatalError("Error at %s:%d: The specified chunk type does not "
+                           "take any flags.\n", file_name, file_line);
+
+            params++;
+        }
+
+        /* Skip any whitespace between the <flags> and <mask> fields. */
+        while (isspace((u_char)*params)) params++;
+
+        /* Parse the mask field, if set. */
+        while ((params < end) && (comma_set == 1))
+        {
+            if (sctd->data)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('i', 'I', mask, DATA, I);
+                    SCTP_CHECK_CHUNK_FLAGS('u', 'U', mask, DATA, U);
+                    SCTP_CHECK_CHUNK_FLAGS('b', 'B', mask, DATA, B);
+                    SCTP_CHECK_CHUNK_FLAGS('e', 'E', mask, DATA, E);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for DATA chunks "
+                               "are IUBE.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->abort)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, ABORT, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for ABORT chunks "
+                               "are T.\n", file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->shutdown_complete)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, SHUTDOWN_COMPLETE, T);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for "
+                               "SHUTDOWN_COMPLETE chunks are T.\n",
+                               file_name, file_line);
+                    break;
+                }
+
+            else if (sctd->pktdrop)
+                switch (*params)
+                {
+                    SCTP_CHECK_CHUNK_FLAGS('c', 'C', mask, PKTDROP, C);
+                    SCTP_CHECK_CHUNK_FLAGS('t', 'T', mask, PKTDROP, T);
+                    SCTP_CHECK_CHUNK_FLAGS('b', 'B', mask, PKTDROP, B);
+                    SCTP_CHECK_CHUNK_FLAGS('m', 'M', mask, PKTDROP, M);
+
+                default:
+                    FatalError("Error at %s:%d: Valid masks for PKTDROP chunks "
+                               "are CTBM.\n", file_name, file_line);
+                    break;
+                }
+            else
+                FatalError("Error at %s:%d: The specified chunk type does not "
+                           "take any masks.\n", file_name, file_line);
+
+            params++;
+        }
+    }
+    else
+        FatalError("Error at %s:%d: When using 'sctp_chunk_flags', the "
+                   "'sctp_chunk_type' keyword can only be set to a single "
+                   "parameter representing the chunk whose flags field "
+                   "you wish to check.\n",  file_name, file_line);
+}
+
+/**
+ * SctpChunkFlagsEval - Evaluation function for sctp_chunk_flags.
+ * @data: pointer to SctpChunkFlagsData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkFlagsEval(void *data, Packet *p)
+{
+    SctpChunkFlagsData *scfd = (SctpChunkFlagsData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || !scfd)
+        return rval;
+
+    PREPROC_PROFILE_START(sctpChunkFlagsPerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Loop through the chunks in the packet and for each chunk, check
+     * check the chunk flags against the supplied value for a match.
+     */
+    while ((c != NULL) &&
+           (remaining_len != 0) &&
+           (rval == DETECTION_OPTION_NO_MATCH))
+    {
+        uint8_t chunk_flags = c->flags & (0xff ^ scfd->mask);
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        /*
+         * Although we have already validated which chunks we want to check
+         * via 'sctp_chunk_type', we still have to check the flags for
+         * specific types because of chunk bundling.
+         */
+        switch (c->type)
+        {
+        case SCTP_DATA_C:
+        case SCTP_ABORT_C:
+        case SCTP_SHUTDOWN_COMPLETE_C:
+        case SCTP_PKTDROP_C:
+            switch (scfd->mode)
+            {
+            case SCTP_FLAGS_NORMAL:
+                if (scfd->flags == chunk_flags)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_ALL:
+                if ((scfd->flags & chunk_flags) == scfd->flags)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_ANY:
+                if ((scfd->flags & chunk_flags) != 0)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+
+            case SCTP_FLAGS_NOT:
+                if ((scfd->flags & chunk_flags) == 0)
+                    rval = DETECTION_OPTION_MATCH;
+                break;
+            }
+            break;
+
+        case SCTP_INIT_C:
+        case SCTP_INIT_ACK_C:
+        case SCTP_SACK_C:
+        case SCTP_HEARTBEAT_C:
+        case SCTP_HEARTBEAT_ACK_C:
+        case SCTP_SHUTDOWN_C:
+        case SCTP_SHUTDOWN_ACK_C:
+        case SCTP_ERROR_C:
+        case SCTP_COOKIE_ECHO_C:
+        case SCTP_COOKIE_ACK_C:
+        case SCTP_ECNE_C:
+        case SCTP_CWR_C:
+        case SCTP_AUTH_C:
+        case SCTP_ASCONF_ACK_C:
+        case SCTP_PAD_C:
+        case SCTP_FORWARD_TSN_C:
+        case SCTP_ASCONF_C:
+            break;
+
+        default:
+            DEBUG_WRAP(DebugMessage(
+                DEBUG_PLUGIN, "Plugin (sctp_chunk_flags): Encountered "
+                              "unknown SCTP Chunk Type 0x%2x!\n", c->type));
+            break;
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkFlagsPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_flags.h b/src/detection-plugins/sp_sctp_chunk_flags.h
new file mode 100644
index 0000000..5de1493
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_flags.h
@@ -0,0 +1,79 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_FLAGS_H__
+#define __SP_SCTP_CHUNK_FLAGS_H__
+
+/* From linux-kernel/include/linux/bitops.h */
+#define BIT(nr) (1UL << (nr))
+
+/* Flag Definitions for DATA. */
+#define SCTP_DATA_C_FLAGS_I                 BIT(3)  /* SACK Immediate */
+#define SCTP_DATA_C_FLAGS_U                 BIT(2)  /* Unordered */
+#define SCTP_DATA_C_FLAGS_B                 BIT(1)  /* Beginning Fragment */
+#define SCTP_DATA_C_FLAGS_E                 BIT(0)  /* Ending Fragment */
+#define SCTP_DATA_C_FLAGS_MASK              0xffff
+
+/* Flag Definitions for ABORT. */
+#define SCTP_ABORT_C_FLAGS_T                BIT(0)  /* VTag Reflected */
+#define SCTP_ABORT_C_FLAGS_MASK             0x0f
+
+/* Flag Definitions for SHUTDOWN-COMPLETE. */
+#define SCTP_SHUTDOWN_COMPLETE_C_FLAGS_T    BIT(0)  /* VTag Reflected */
+#define SCTP_SHUTDOWN_COMPLETE_C_FLAGS_MASK 0x0f
+
+/* Flag Definitions for PKTDROP. */
+#define SCTP_PKTDROP_C_FLAGS_C              BIT(3)  /* Count Transform */
+#define SCTP_PKTDROP_C_FLAGS_T              BIT(2)  /* Truncated */
+#define SCTP_PKTDROP_C_FLAGS_B              BIT(1)  /* Bad CRC32c */
+#define SCTP_PKTDROP_C_FLAGS_M              BIT(0)  /* Middle/VTag Reflected */
+#define SCTP_PKTDROP_C_FLAGS_MASK           0xffff
+
+/* Flag Mode Definitions. */
+#define SCTP_FLAGS_NORMAL                   0x00
+#define SCTP_FLAGS_ALL                      0x01
+#define SCTP_FLAGS_ANY                      0x02
+#define SCTP_FLAGS_NOT                      0x04
+
+/* Macros to duplicate common bits of code and make things look cleaner. */
+#define SCTP_CHECK_CHUNK_FLAGS(lower,upper,field,chunk,bit)            \
+                case lower:                                            \
+                case upper:                                            \
+                    scfd->field |= SCTP_##chunk##_C_FLAGS_##bit;       \
+                    break
+
+
+/* SctpChunkFlags data structure. */
+typedef struct _SctpChunkFlagsData
+{
+    u_char mode;
+    u_char flags;
+    u_char mask;
+} SctpChunkFlagsData;
+
+/* Prototypes. */
+uint32_t SctpChunkFlagsHash(void *data);
+int SctpChunkFlagsCompare(void *l, void *r);
+void SctpChunkFlagsSetup(void);
+void SctpChunkFlagsInit(char *data, OptTreeNode *otn, int protocol);
+void SctpChunkFlagsParse(char *data, OptTreeNode *otn);
+int SctpChunkFlagsEval(void *data, Packet *p);
+
+#endif  /* __SP_SCTP_CHUNK_FLAGS_H__ */
diff --git a/src/detection-plugins/sp_sctp_chunk_type.c b/src/detection-plugins/sp_sctp_chunk_type.c
new file mode 100644
index 0000000..ad3b4bc
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_type.c
@@ -0,0 +1,469 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "sp_sctp_chunk_type.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpChunkTypePerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/**
+ * SctpChunkTypeHash - Hash function for sctp_chunk_type.
+ * @data: pointer to SctpChunkFlagsData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpChunkTypeHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpChunkTypeData *sctd = (SctpChunkTypeData *)data;
+
+    a = sctd->data;
+    b = sctd->init;
+    c = sctd->init_ack;
+    mix(a, b, c);
+    a += sctd->sack;
+    b += sctd->heartbeat;
+    c += sctd->heartbeat_ack;
+    mix(a, b, c);
+    a += sctd->abort;
+    b += sctd->shutdown;
+    c += sctd->shutdown_ack;
+    mix(a, b, c);
+    a += sctd->error;
+    b += sctd->cookie_echo;
+    c += sctd->cookie_ack;
+    mix(a, b, c);
+    a += sctd->ecne;
+    b += sctd->cwr;
+    c += sctd->shutdown_complete;
+    mix(a, b, c);
+    a += sctd->auth;
+    b += sctd->asconf_ack;
+    c += sctd->pktdrop;
+    mix(a, b, c);
+    a += sctd->pad;
+    b += sctd->forward_tsn;
+    c += sctd->asconf;
+    mix(a, b, c);
+    a += RULE_OPTION_TYPE_SCTP_CHUNK_TYPE;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpChunkTypeCompare - Comparator function for sctp_chunk_type.
+ * @l: pointer to SctpChunkTypeData (left side).
+ * @r: pointer to SctpChunkTypeData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpChunkTypeCompare(void *l, void *r)
+{
+    SctpChunkTypeData *left = (SctpChunkTypeData *)l;
+    SctpChunkTypeData *right = (SctpChunkTypeData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->data != right->data)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->init != right->init)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->init_ack != right->init_ack)                   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->sack != right->sack)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->heartbeat != right->heartbeat)                 return DETECTION_OPTION_NOT_EQUAL;
+    if (left->heartbeat_ack != right->heartbeat_ack)         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->abort != right->abort)                         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown != right->shutdown)                   return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown_ack != right->shutdown_ack)           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->error != right->error)                         return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cookie_echo != right->cookie_echo)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cookie_ack != right->cookie_ack)               return DETECTION_OPTION_NOT_EQUAL;
+    if (left->ecne != right->ecne)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->cwr != right->cwr)                             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->shutdown_complete != right->shutdown_complete) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->auth != right->auth)                           return DETECTION_OPTION_NOT_EQUAL;
+    if (left->asconf_ack != right->asconf_ack)               return DETECTION_OPTION_NOT_EQUAL;
+    if (left->pktdrop != right->pktdrop)                     return DETECTION_OPTION_NOT_EQUAL;
+    if (left->pad != right->pad)                             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->forward_tsn != right->forward_tsn)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->asconf != right->asconf)                       return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpChunkTypeSetup - Setup function for sctp_chunk_type.
+ *
+ * Links the 'sctp_chunk_type' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpChunkTypeSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_chunk_type", SctpChunkTypeInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_chunk_type", &sctpChunkTypePerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpChunkType Initialized\n"););
+}
+
+/**
+ * SctpChunkTypeInit - Initialization function for sctp_chunk_type.
+ * @data: pointer to SctpChunkTypeData (as char *).
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpChunkTypeInit(char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpChunkTypeData *sctd;
+    void *sctd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_chunk_types' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE])
+        FatalError("%s(%d): 'sctp_chunk_type' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] = (SctpChunkTypeData *)
+            SnortAlloc(sizeof(SctpChunkTypeData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpChunkTypeParse(data, otn);
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(RULE_OPTION_TYPE_SCTP_CHUNK_TYPE,
+                             (void *)sctd, &sctd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE] = (SctpChunkTypeData *)sctd_dup;
+        free(sctd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpChunkTypeEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_CHUNK_TYPE;
+    fpl->context = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+}
+
+/**
+ * SctpChunkTypeParse - Parsing function for sctp_chunk_type.
+ * @data: char pointer to SctpChunkTypeData.
+ * @otn: pointer to the OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpChunkTypeData
+ * structure.  Returns void.
+ */
+void SctpChunkTypeParse(char *data, OptTreeNode *otn)
+{
+    char *tok, *params;
+    SctpChunkTypeData *sctd;
+
+    /* Set the sctd pointer to make it easier to reference the option's
+       particular data struct */
+    sctd = otn->ds_list[PLUGIN_SCTP_CHUNK_TYPE];
+
+    params = SnortStrdup(data);
+    while(isspace((int)*params)) params++;     /* nuke leading whitespace */
+    
+    /* Make sure at least one value was supplied. */
+    tok = strtok(params, ",");
+    if (!tok)
+        FatalError("Error at %s:%d: Missing argument to the 'sctp_chunk_type' "
+                   "keyword\n", file_name, file_line);
+
+    /* Negation - Only handled on the first token. */
+    if (tok[0] == '!')
+    {
+        sctd->neg = 1;
+        tok++;
+    }
+
+    /* Parse each token. */
+    while (tok)
+    {
+        /* Limit the number of tokens to be (SCTP_MAX_NUM_CHUNKS - 1). */
+        if (sctd->parse_count >= SCTP_MAX_NUM_CHUNKS)
+            FatalError("Error at %s:%d: Number of parameters exceeds "
+                       "SCTP_MAX_NUM_CHUNKS (%d)!\n", file_name,
+                       file_line, SCTP_MAX_NUM_CHUNKS);
+
+        /*
+         * For each token, See what the specified chunk type is and set its
+         * appropriate member in the sctd structure to be 'true'.
+         */
+        if      (!strcasecmp("DATA", tok))              sctd->data = 1;
+        else if (!strcasecmp("INIT", tok))              sctd->init = 1;
+        else if (!strcasecmp("INIT-ACK", tok))          sctd->init_ack = 1;
+        else if (!strcasecmp("SACK", tok))              sctd->sack = 1;
+        else if (!strcasecmp("HEARTBEAT", tok))         sctd->heartbeat = 1;
+        else if (!strcasecmp("HEARTBEAT-ACK", tok))     sctd->heartbeat_ack = 1;
+        else if (!strcasecmp("ABORT", tok))             sctd->abort = 1;
+        else if (!strcasecmp("SHUTDOWN", tok))          sctd->shutdown = 1;
+        else if (!strcasecmp("SHUTDOWN-ACK", tok))      sctd->shutdown_ack = 1;
+        else if (!strcasecmp("ERROR", tok))             sctd->error = 1;
+        else if (!strcasecmp("COOKIE-ECHO", tok))       sctd->cookie_echo = 1;
+        else if (!strcasecmp("COOKIE-ACK", tok))        sctd->cookie_ack = 1;
+        else if (!strcasecmp("ECNE", tok))              sctd->ecne = 1;
+        else if (!strcasecmp("CWR", tok))               sctd->cwr = 1;
+        else if (!strcasecmp("SHUTDOWN-COMPLETE", tok)) sctd->shutdown_complete = 1;
+        else if (!strcasecmp("AUTH", tok))              sctd->auth = 1;
+        else if (!strcasecmp("ASCONF-ACK", tok))        sctd->asconf_ack = 1;
+        else if (!strcasecmp("PKTDROP", tok))           sctd->pktdrop = 1;
+        else if (!strcasecmp("PAD", tok))               sctd->pad = 1;
+        else if (!strcasecmp("FORWARD-TSN", tok))       sctd->forward_tsn = 1;
+        else if (!strcasecmp("ASCONF", tok))            sctd->asconf = 1;
+        else
+            FatalError("Error at %s:%d: %s is not a recognized argument to "
+                       "'sctp_chunk_type'.\n", file_name, file_line, tok);
+
+        sctd->parse_count++;
+        tok = strtok(NULL, ",");
+    }
+}
+
+
+/**
+ * SctpChunkTypeEval - Evaluation function for sctp_chunk_type.
+ * @data: void pointer to SctpChunkTypeData.
+ * @p: pointer to the Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpChunkTypeEval(void *data, Packet *p)
+{
+    SctpChunkTypeData *sctd = (SctpChunkTypeData *)data;
+    int rval;
+    PROFILE_VARS;
+
+    if(!p->sctph || !sctd)
+        return DETECTION_OPTION_NO_MATCH;
+
+    PREPROC_PROFILE_START(sctpChunkTypePerfStats);
+
+    SctpChunk *c = (SctpChunk *)p->sctph->chunks;
+    uint32_t remaining_len = p->dsize;
+
+    /*
+     * Pre-assignment for rval.  Yes, it's inverted.  Don't try to think
+     * about it, because you'll only wind up getting hurt.  There is probably
+     * a better way to do this.
+     */
+    rval = (sctd->neg ? DETECTION_OPTION_MATCH :
+                        DETECTION_OPTION_NO_MATCH);
+
+    /*
+     * For each chunk, look at its type field and find the corresponding
+     * member in the SctpChunkTypeData struct.  If it is true, then see
+     * whether we have to deal with negation.
+     */
+    while ((c != NULL) && (remaining_len != 0))
+    {
+        uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
+        remaining_len -= chunk_length;
+        
+        switch (c->type)
+        {
+            case SCTP_DATA_C:
+                if (sctd->data)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_INIT_C:
+                if (sctd->init)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_INIT_ACK_C:
+                if (sctd->init_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SACK_C:
+                if (sctd->sack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_HEARTBEAT_C:
+                if (sctd->heartbeat)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_HEARTBEAT_ACK_C:
+                if (sctd->heartbeat_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ABORT_C:
+                if (sctd->abort)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_C:
+                if (sctd->shutdown)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_ACK_C:
+                if (sctd->shutdown_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ERROR_C:
+                if (sctd->error)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_COOKIE_ECHO_C:
+                if (sctd->cookie_echo)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_COOKIE_ACK_C:
+                if (sctd->cookie_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ECNE_C:
+                if (sctd->ecne)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_CWR_C:
+                if (sctd->cwr)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_SHUTDOWN_COMPLETE_C:
+                if (sctd->shutdown_complete)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_AUTH_C:
+                if (sctd->auth)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ASCONF_ACK_C:
+                if (sctd->asconf_ack)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_PKTDROP_C:
+                if (sctd->pktdrop)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_PAD_C:
+                if (sctd->pad)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_FORWARD_TSN_C:
+                if (sctd->forward_tsn)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            case SCTP_ASCONF_C:
+                if (sctd->asconf)
+                    rval = (sctd->neg ? DETECTION_OPTION_NO_MATCH :
+                                        DETECTION_OPTION_MATCH);
+                break;
+
+            default:
+                DEBUG_WRAP(DebugMessage(
+                    DEBUG_PLUGIN, "Plugin (sctp_chunk_type): Encountered "
+                                  "unknown SCTP Chunk Type 0x%2x!\n",
+                                  c->type));
+                break;
+        }
+
+        remaining_len = SctpGetNextChunk(c, remaining_len, chunk_length);
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpChunkTypePerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_chunk_type.h b/src/detection-plugins/sp_sctp_chunk_type.h
new file mode 100644
index 0000000..6c93fa6
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_chunk_type.h
@@ -0,0 +1,63 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_CHUNK_TYPE_H__
+#define __SP_SCTP_CHUNK_TYPE_H__
+
+/* Current number of chunks, defined by approved RFC's */
+#define SCTP_MAX_NUM_CHUNKS         21
+
+/* SctpChunkType data structure. */
+typedef struct _SctpChunkTypeData
+{
+    uint8_t parse_count;
+    uint8_t neg;
+    uint8_t data;
+    uint8_t init;
+    uint8_t init_ack;
+    uint8_t sack;
+    uint8_t heartbeat;
+    uint8_t heartbeat_ack;
+    uint8_t abort;
+    uint8_t shutdown;
+    uint8_t shutdown_ack;
+    uint8_t error;
+    uint8_t cookie_echo;
+    uint8_t cookie_ack;
+    uint8_t ecne;
+    uint8_t cwr;
+    uint8_t shutdown_complete;
+    uint8_t auth;
+    uint8_t asconf_ack;
+    uint8_t pktdrop;
+    uint8_t pad;
+    uint8_t forward_tsn;
+    uint8_t asconf;
+} SctpChunkTypeData;
+
+/* Prototypes. */
+uint32_t SctpChunkTypeHash(void *data);
+int SctpChunkTypeCompare(void *l, void *r);
+void SctpChunkTypeSetup(void);
+void SctpChunkTypeInit(char *data, OptTreeNode *otn, int protocol);
+void SctpChunkTypeParse(char *data, OptTreeNode *otn);
+int SctpChunkTypeEval(void *data, Packet *p);
+
+#endif  /* __SP_SCTP_CHUNK_TYPE_H__ */
diff --git a/src/detection-plugins/sp_sctp_num_chunks.c b/src/detection-plugins/sp_sctp_num_chunks.c
new file mode 100644
index 0000000..69d8ef5
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_num_chunks.c
@@ -0,0 +1,281 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sf_types.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "decode.h"
+#include "plugbase.h"
+#include "parser.h"
+#include "util.h"
+#include "snort_debug.h"
+#include "plugin_enum.h"
+#include "sp_sctp_num_chunks.h"
+
+#include "snort.h"
+#include "profiler.h"
+#ifdef PERF_PROFILING
+PreprocStats sctpNumChunksPerfStats;
+extern PreprocStats ruleOTNEvalPerfStats;
+#endif
+
+#include "sfhashfcn.h"
+#include "detection_options.h"
+
+
+/**
+ * SctpNumChunksHash - Hash function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData.
+ *
+ * Returns a uint32_t representing the hash.
+ */
+uint32_t SctpNumChunksHash(void *data)
+{
+    uint32_t a, b, c;
+    SctpNumChunksData *sncd = (SctpNumChunksData *)data;
+
+    a = sncd->num_chunks_min;
+    b = sncd->num_chunks_max;
+    c = (uint32_t)sncd->operator;
+    mix(a, b, c);
+
+    a += RULE_OPTION_TYPE_SCTP_NUM_CHUNKS;
+    final(a, b, c);
+
+    return c;
+}
+
+/**
+ * SctpNumChunksCompare - Comparator function for sctp_num_chunks.
+ * @l: pointer to SctpNumChunksData (left side).
+ * @r: pointer to SctpNumChunksData (right side).
+ *
+ * Returns the comparison result of the two data structures.
+ */
+int SctpNumChunksCompare(void *l, void *r)
+{
+    SctpNumChunksData *left = (SctpNumChunksData *)l;
+    SctpNumChunksData *right = (SctpNumChunksData *)r;
+
+    if (!left)
+        return (!right ? DETECTION_OPTION_EQUAL : 
+                         DETECTION_OPTION_NOT_EQUAL);
+    
+    if (!right)
+        return (!left ? DETECTION_OPTION_EQUAL : 
+                        DETECTION_OPTION_NOT_EQUAL);
+
+    if (left->operator != right->operator)             return DETECTION_OPTION_NOT_EQUAL;
+    if (left->num_chunks_min != right->num_chunks_min) return DETECTION_OPTION_NOT_EQUAL;
+    if (left->num_chunks_max != right->num_chunks_max) return DETECTION_OPTION_NOT_EQUAL;
+
+    return DETECTION_OPTION_EQUAL;
+}
+
+/**
+ * SctpNumChunksSetup - Setup function for sctp_num_chunks.
+ *
+ * Links the 'sctp_num_chunks' keyword to its initialization function.
+ * Returns void.
+ */
+void SctpNumChunksSetup(void)
+{
+    /* Map the keyword to an initialization/processing function */
+    RegisterRuleOption("sctp_num_chunks", SctpNumChunksInit, NULL, OPT_TYPE_DETECTION, NULL);
+    
+#ifdef PERF_PROFILING
+    RegisterPreprocessorProfile("sctp_num_chunks", &sctpNumChunksPerfStats, 3, &ruleOTNEvalPerfStats);
+#endif
+
+    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: SctpNumChunks Initialized\n"););
+}
+
+/**
+ * SctpNumChunksInit - Initialization function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData.
+ * @otn: pointer to OptTreeNode structure.
+ * @protocol: integer value representing the transport-layer protocol.
+ *
+ * Attaches the rule option data to the appropriate data structure and links
+ * in the evaluator function to the function pointer list.  Returns void.
+ */
+void SctpNumChunksInit(char *data, OptTreeNode *otn, int protocol)
+{
+    OptFpList *fpl;
+    SctpNumChunksData *sncd;
+    void *sncd_dup;
+
+    /* This keyword only applies to the SCTP Protocol. */
+    if (protocol != IPPROTO_SCTP)
+        FatalError("Error at %s:%d: 'sctp_num_chunks' can only be used with "
+                   "the SCTP protocol.\n", file_name, file_line);
+
+    /* Check for multiple uses of this keyword in a rule. */
+    if (otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS])
+        FatalError("Error at %s:%d: 'sctp_num_chunks' may only be used once per "
+                   "rule.\n", file_name, file_line);
+
+    /* Allocate the data structure and attach it to the otn->ds_list. */
+    otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS] = (SctpNumChunksData *)
+            SnortAlloc(sizeof(SctpNumChunksData));
+
+    /* Parse the keyword's parameters and configure the data structure. */
+    SctpNumChunksParse(data, otn);
+    sncd = otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+
+    /* Check for a duplicate entry in the otn. */
+    if (add_detection_option(RULE_OPTION_TYPE_SCTP_NUM_CHUNKS,
+                             (void *)sncd, &sncd_dup) == DETECTION_OPTION_EQUAL)
+    {
+        otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS] = (SctpNumChunksData *)sncd_dup;
+        free(sncd);
+    }
+
+    /*
+     * Finally, attach the option's detection function to the rule's detection
+     * function pointer list
+     */
+    fpl = AddOptFuncToList(SctpNumChunksEval, otn);
+    fpl->type = RULE_OPTION_TYPE_SCTP_NUM_CHUNKS;
+    fpl->context = (void *)otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+}
+
+/**
+ * SctpNumChunksParse - Parsing function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData.
+ * @otn: pointer to OptTreeNode structure.
+ *
+ * Parses the rule option's parameter list and fills in the SctpNumChunksData
+ * structure.  Returns void.
+ */
+void SctpNumChunksParse(char *data, OptTreeNode *otn)
+{
+    char *tok = data, *end;
+    int num = 0, rval = 0;
+    UNumericRange rng;
+    SctpNumChunksData *sncd;
+
+    /* Get the sctp_num_chunks data set. */
+    sncd = otn->ds_list[PLUGIN_SCTP_NUM_CHUNKS];
+
+    while(isspace((int)*tok)) tok++;
+
+    /* Parse out the operands of the range. */
+    rval = SnortStrToUNumRng(tok, "<>", &rng);
+    if (rval == SNORT_STR2RNG_SUCCESS)
+    {
+        sncd->num_chunks_min = rng.min;
+        sncd->num_chunks_max = rng.max;
+        sncd->operator = SCTP_NUM_CHUNKS_RANGE;
+    }
+
+    /* Not a ranged value. */
+    else if (rval == SNORT_STR2RNG_NOTRNG)
+    {
+        if (*tok == '>')
+        {
+            tok++;
+            sncd->operator = SCTP_NUM_CHUNKS_GT;
+        }
+        else if(*tok == '<')
+        {
+            tok++;
+            sncd->operator = SCTP_NUM_CHUNKS_LT;
+        }
+        else
+            sncd->operator = SCTP_NUM_CHUNKS_EQUAL;
+    }
+
+    /* Error. */
+    else if (rval == SNORT_STR2RNG_ERROR)
+        goto error;
+
+    /* Parse the remainder and store it as the minimum value. */
+    num = strtol(tok, &end, 10);
+    if (*end)
+        goto error;
+
+    sncd->num_chunks_min = num;
+
+    return;
+
+error:
+    FatalError("Error at %s:%d: Invalid 'sctp_num_chunks' argument.\n",
+               file_name, file_line);
+    return;
+}
+
+/**
+ * SctpNumChunksEval - Evaluation function for sctp_num_chunks.
+ * @data: pointer to SctpNumChunksData (as char *).
+ * @p: pointer to Packet structure.
+ *
+ * Evaluates the rule option against the packet pointed to by 'p'.  Returns
+ * either DETECTION_OPTION_MATCH or DETECTION_OPTION_NO_MATCH.
+ */
+int SctpNumChunksEval(void *data, Packet *p)
+{
+    SctpNumChunksData *sncd = (SctpNumChunksData *)data;
+    int rval = DETECTION_OPTION_NO_MATCH;
+    PROFILE_VARS;
+
+    if(!p->sctph || !sncd)
+        return rval;
+
+    PREPROC_PROFILE_START(sctpNumChunksPerfStats);
+
+    /* The chunk count is maintained in Packet *p. */
+    switch (sncd->operator)
+    {
+        case SCTP_NUM_CHUNKS_EQUAL:
+            if (sncd->num_chunks_min == p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_GT:
+            if (sncd->num_chunks_min < p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_LT:
+            if (sncd->num_chunks_min > p->sctp_chunk_count)
+                rval = DETECTION_OPTION_MATCH;
+            break;
+
+        case SCTP_NUM_CHUNKS_RANGE:
+            if ((sncd->num_chunks_min <= p->sctp_chunk_count) &&
+                (sncd->num_chunks_max >= p->sctp_chunk_count))
+                rval = DETECTION_OPTION_MATCH;
+            break;
+    }
+
+    /* If the test isn't successful, return DETECTION_OPTION_NO_MATCH */
+    PREPROC_PROFILE_END(sctpNumChunksPerfStats);
+    return rval;
+}
diff --git a/src/detection-plugins/sp_sctp_num_chunks.h b/src/detection-plugins/sp_sctp_num_chunks.h
new file mode 100644
index 0000000..b3ff016
--- /dev/null
+++ b/src/detection-plugins/sp_sctp_num_chunks.h
@@ -0,0 +1,46 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 () gmail com>
+**
+** 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.
+*/
+
+/* $Id$ */
+#ifndef __SP_SCTP_NUM_CHUNKS_H__
+#define __SP_SCTP_NUM_CHUNKS_H__
+
+/* Operator definitions for sctp_payload_id. */
+#define SCTP_NUM_CHUNKS_EQUAL               0x01
+#define SCTP_NUM_CHUNKS_LT                  0x02
+#define SCTP_NUM_CHUNKS_GT                  0x03
+#define SCTP_NUM_CHUNKS_RANGE               0x04
+
+/* SctpPayloadId data structure. */
+typedef struct _SctpNumChunksData
+{
+    uint32_t num_chunks_min;
+    uint32_t num_chunks_max;
+    uint8_t operator;
+} SctpNumChunksData;
+
+/* Prototypes. */
+uint32_t SctpNumChunksHash(void *data);
+int SctpNumChunksCompare(void *l, void *r);
+void SctpNumChunksSetup(void);
+void SctpNumChunksInit(char *data, OptTreeNode *otn, int protocol);
+void SctpNumChunksParse(char *data, OptTreeNode *otn);
+int SctpNumChunksEval(void *data, Packet *p);
+
+#endif  /* __SP_SCTP_NUM_CHUNKS_H__ */
diff --git a/src/dynamic-plugins/sf_engine/sf_snort_packet.h b/src/dynamic-plugins/sf_engine/sf_snort_packet.h
index ba6da44..b229cd3 100644
--- a/src/dynamic-plugins/sf_engine/sf_snort_packet.h
+++ b/src/dynamic-plugins/sf_engine/sf_snort_packet.h
@@ -382,6 +382,148 @@ typedef struct _ICMP6
 struct _SFSnortPacket;
 
 
+/* Snort redefines much of decoder.h here for unknown reasons. */
+typedef struct _SCTPIpParam2
+{
+    u_int16_t ptype;
+	u_int16_t plen;
+	union
+	{
+		struct _ipv4
+		{
+			u_int32_t address;
+			u_int32_t padding1;
+			u_int32_t padding2;
+			u_int32_t padding3;
+		} ipv4;
+		struct in6_addr ipv6;
+	} address;
+} SCTPIpParam2;
+
+typedef struct _SCTPHeader
+{
+    u_int16_t source_port;          /* source port */
+    u_int16_t destination_port;     /* destination port */
+    u_int32_t verification_tag;     /* verification tag */
+    u_int32_t checksum;             /* checksum */
+	u_int8_t chunk_type;            /* chunk type */
+	u_int8_t chunk_flags;           /* chunk flags */
+	u_int16_t chunk_length;         /* chunk length */
+    void *chunks;                   /* chunk(s) data pointer */
+} SCTPHeader;
+
+typedef union _SCTPChunkData
+{
+    struct data_chunk
+    {
+        u_int32_t tsn;
+        u_int16_t stream_id;
+        u_int16_t stream_seq;
+        u_int32_t pp_id;
+        void *data;
+    } data;
+
+    struct init_chunk
+    {
+        u_int32_t init_tag;
+        u_int32_t a_rwnd;
+        u_int16_t num_out_streams;
+        u_int16_t num_in_streams;
+        u_int32_t tsn;
+        void *parameters;
+    } init;
+
+    struct init_ack_chunk
+    {
+        u_int32_t init_tag;
+        u_int32_t a_rwnd;
+        u_int16_t num_out_streams;
+        u_int16_t num_in_streams;
+        u_int32_t tsn;
+        void *parameters;
+    } init_ack;
+
+    struct sack_chunk
+    {
+        u_int32_t tsn_ack;
+        u_int32_t a_rwnd;
+        u_int16_t n_gap_acks;
+        u_int16_t n_dupe_tsns;
+        void *data;
+    } sack;
+
+    struct heartbeat_chunk
+    {
+        void *parameters;
+    } heartbeat;
+
+    struct heartbeat_ack_chunk
+    {
+        void *parameters;
+    } heartbeat_ack;
+
+    struct abort_chunk
+    {
+        void *causes;
+    } abort;
+
+    struct shutdown_chunk
+    {
+        u_int32_t tsn_ack;
+    } shutdown;
+
+    /* No struct for shutdown_ack. */
+
+    struct error_chunk
+    {
+        void *errors;
+    } error;
+
+    struct cookie_echo_chunk
+    {
+        char *cookie;
+    } cookie_echo;
+
+    /* No struct for cookie_ack. */
+
+    /* No struct for ecne (reserved). */
+
+    /* No struct for cwr (reserved). */
+
+    /* No struct for shutdown_complete. */
+
+    struct auth_chunk
+    {
+        u_int16_t skey_id;
+        u_int16_t hmac_id;
+        void *hmac;
+    } auth;
+
+    struct asconf_ack_chunk
+    {
+        u_int32_t seq;
+        void *parameters;
+    } asconf_ack;
+
+    struct pad_chunk
+    {
+        void *data;
+    } pad;
+
+    struct forward_tsn_chunk
+    {
+        u_int32_t new_tsn;
+        void *streams;
+    } forward_tsn;
+
+    struct asconf_chunk
+    {
+        u_int32_t seq;
+        SCTPIpParam2 *addr;
+        void *parameters;
+    } asconf;
+} SCTPChunkData;
+
 /* IPHeader access calls */
 sfip_t *    ip4_ret_src(const struct _SFSnortPacket *);
 sfip_t *    ip4_ret_dst(const struct _SFSnortPacket *);
@@ -521,6 +663,7 @@ typedef struct _SFSnortPacket
     const UDPHeader *inner_udph;   /* if Teredo + UDP, this will be the inner UDP header */
     const UDPHeader *outer_udph;   /* if Teredo + UDP, this will be the outer UDP header */
     const ICMPHeader *icmp_header, *orig_icmp_header;
+    const SCTPHeader *sctp_header, *orig_sctp_header;
 
     const uint8_t *payload;
     const uint8_t *ip_payload;
@@ -665,14 +808,16 @@ typedef struct _SFSnortPacket
 #define PROTO_BIT__ARP      0x0002
 #define PROTO_BIT__TCP      0x0004
 #define PROTO_BIT__UDP      0x0008
-#define PROTO_BIT__ICMP     0x0010
-#define PROTO_BIT__TEREDO   0x0020
+#define PROTO_BIT__SCTP     0x0010
+#define PROTO_BIT__ICMP     0x0020
+#define PROTO_BIT__TEREDO   0x0040
 #define PROTO_BIT__ALL      0xffff
 
 #define IsIP(p) (IPH_IS_VALID(p))
 #define IsTCP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_TCP))
 #define IsUDP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_UDP))
 #define IsICMP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_ICMP))
+#define IsSCTP(p) (IsIP(p) && (GET_IPH_PROTO(p) == IPPROTO_SCTP))
 
 #define SET_IP4_VER(ip_header, value) \
     ((ip_header)->version_headerlength = \
diff --git a/src/fpcreate.c b/src/fpcreate.c
index 39b550c..6f48dc1 100644
--- a/src/fpcreate.c
+++ b/src/fpcreate.c
@@ -237,6 +237,9 @@ static srmm_table_t * ServiceMapNew(void)
     table->udp_to_srv = alloc_srvmap();
     table->udp_to_cli = alloc_srvmap();
 
+    table->sctp_to_srv = alloc_srvmap();
+    table->sctp_to_cli = alloc_srvmap();
+
     table->icmp_to_srv = alloc_srvmap();
     table->icmp_to_cli = alloc_srvmap();
 
@@ -261,6 +264,8 @@ static void ServiceMapFree(srmm_table_t *srvc_map)
     ServiceTableFree(srvc_map->tcp_to_cli);
     ServiceTableFree(srvc_map->udp_to_srv);
     ServiceTableFree(srvc_map->udp_to_cli);
+    ServiceTableFree(srvc_map->sctp_to_srv);
+    ServiceTableFree(srvc_map->sctp_to_cli);
     ServiceTableFree(srvc_map->icmp_to_srv);
     ServiceTableFree(srvc_map->icmp_to_cli);
     ServiceTableFree(srvc_map->ip_to_srv);
@@ -297,6 +302,9 @@ static srmm_table_t * ServicePortGroupMapNew(void)
     table->udp_to_srv = alloc_spgmm();
     table->udp_to_cli = alloc_spgmm();
 
+    table->sctp_to_srv = alloc_spgmm();
+    table->sctp_to_cli = alloc_spgmm();
+
     table->icmp_to_srv = alloc_spgmm();
     table->icmp_to_cli = alloc_spgmm();
 
@@ -341,6 +349,8 @@ static void ServicePortGroupMapFree(srmm_table_t *srvc_pg_map)
     ServicePortGroupTableFree(srvc_pg_map->tcp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->udp_to_srv);
     ServicePortGroupTableFree(srvc_pg_map->udp_to_cli);
+    ServicePortGroupTableFree(srvc_pg_map->sctp_to_srv);
+    ServicePortGroupTableFree(srvc_pg_map->sctp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->icmp_to_srv);
     ServicePortGroupTableFree(srvc_pg_map->icmp_to_cli);
     ServicePortGroupTableFree(srvc_pg_map->ip_to_srv);
@@ -408,6 +418,11 @@ static int ServiceMapAddOtn(srmm_table_t *srmm, int proto, char *servicename, Op
         to_srv = srmm->udp_to_srv;
         to_cli = srmm->udp_to_cli;
     }
+    else if( proto == IPPROTO_SCTP)
+    {
+        to_srv = srmm->sctp_to_srv;
+        to_cli = srmm->sctp_to_cli;
+    }
     else if( proto == IPPROTO_ICMP )
     {
         to_srv = srmm->icmp_to_srv;
@@ -473,6 +488,12 @@ int prmFindRuleGroupUdp(PORT_RULE_MAP *prm, int dport, int sport, PORT_GROUP **
     return prmFindRuleGroup( prm, dport, sport, src, dst , gen);
 }
 
+int prmFindRuleGroupSctp(PORT_RULE_MAP *prm, int dport, int sport, PORT_GROUP ** src,
+                        PORT_GROUP **dst , PORT_GROUP ** gen)
+{
+    return prmFindRuleGroup( prm, dport, sport, src, dst , gen);
+}
+
 void free_detection_option_root(void **existing_tree)
 {
     detection_option_tree_root_t *root;
@@ -1858,7 +1879,7 @@ static int fpAddPortGroupRule(PORT_GROUP *pg, OptTreeNode *otn, FastPatternConfi
  *
  *  Generation of PortRuleMaps and data is done differently.
  *
- *    1) Build tcp/udp/icmp/ip src and dst PORT_GROUP objects based on the PortList Objects rules.
+ *    1) Build tcp/udp/sctp/icmp/ip src and dst PORT_GROUP objects based on the PortList Objects rules.
  *
  *    2) For each protocols PortList objects walk it's ports and assign the PORT_RULE_MAP src and dst
  *         PORT_GROUP[port] array pointers to that PortList objects PORT_GROUP.
@@ -1868,7 +1889,7 @@ static int fpAddPortGroupRule(PORT_GROUP *pg, OptTreeNode *otn, FastPatternConfi
  *    Each PortList Object will be translated into a PORT_GROUP, than pointed to by the
  *    PORT_GROUP array in the PORT_RULE_MAP for the procotocol
  *
- *    protocol = tcp, udp, ip, icmp - one port_rule_map for each of these protocols
+ *    protocol = tcp, udp, sctp, ip, icmp - one port_rule_map for each of these protocols
  *    { create a port_rule_map
  *      dst port processing
  *          for each port-list object create a port_group object
@@ -2037,6 +2058,13 @@ static int fpCreateRuleMaps(SnortConfig *sc, rule_port_tables_t *p)
     if (fpCreateInitRuleMap(sc->prmUdpRTNX, p->udp_src, p->udp_dst, p->udp_anyany,p->udp_nocontent))
         return -1;
 
+    sc->prmSctpRTNX = prmNewMap();
+    if (sc->prmSctpRTNX == NULL)
+        return -1;
+
+    if (fpCreateInitRuleMap(sc->prmSctpRTNX, p->sctp_src, p->sctp_dst, p->sctp_anyany,p->sctp_nocontent))
+        return -1;
+
     sc->prmIpRTNX = prmNewMap();
     if (sc->prmIpRTNX == NULL)
         return 1;
@@ -2071,6 +2099,12 @@ static void fpFreeRuleMaps(SnortConfig *sc)
         sc->prmUdpRTNX = NULL;
     }
 
+    if (sc->prmSctpRTNX != NULL)
+    {
+        free(sc->prmSctpRTNX);
+        sc->prmSctpRTNX = NULL;
+    }
+
     if (sc->prmIpRTNX != NULL)
     {
         free(sc->prmIpRTNX);
@@ -2554,7 +2588,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortTablePortGroups(sc, p->udp_dst, add_any_any))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-udp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-udp_dst\n");
         return -1;
     }
 
@@ -2563,7 +2597,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortObject2PortGroup(sc, po2, 0))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-udp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-udp_any-any\n");
         return -1;
     }
 
@@ -2573,6 +2607,47 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
     //LogMessage("fpcreate: calling PortObjectFree2(po2), line = %d\n",__LINE__ );
     PortObject2Free(po2);
 
+    /* SCTP */
+    po2 = PortObject2Dup(p->sctp_anyany);
+    if (po2 == NULL )
+        FatalError("Could not create a PortObject version 2 for sctp-any-any rules\n!");
+
+    if (!fpDetectSplitAnyAny(fp))
+        add_any_any = po2;
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-SRC ");
+
+    if (fpCreatePortTablePortGroups(sc, p->sctp_src, add_any_any))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_src\n");
+        return -1;
+    }
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-DST ");
+
+    if (fpCreatePortTablePortGroups(sc, p->sctp_dst, add_any_any))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_dst\n");
+        return -1;
+    }
+
+    if (fpDetectGetDebugPrintRuleGroupBuildDetails(fp))
+        LogMessage("\nSCTP-ANYANY ");
+
+    if (fpCreatePortObject2PortGroup(sc, po2, 0))
+    {
+        LogMessage("fpCreatePorTablePortGroups failed-sctp_any-any\n");
+        return -1;
+    }
+
+    p->sctp_anyany->data = po2->data;
+    p->sctp_anyany->data_free = fpDeletePortGroup;
+    po2->data = 0;
+    //LogMessage("fpcreate: calling PortObjectFree2(po2), line = %d\n",__LINE__ );
+    PortObject2Free(po2);
+
     /* ICMP */
     po2 = PortObject2Dup(p->icmp_anyany);
     if (po2 == NULL)
@@ -2595,7 +2670,7 @@ static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
 
     if (fpCreatePortTablePortGroups(sc, p->icmp_dst, add_any_any))
     {
-        LogMessage("fpCreatePorTablePortGroups failed-icmp_src\n");
+        LogMessage("fpCreatePorTablePortGroups failed-icmp_dst\n");
         return -1;
     }
 
@@ -2695,7 +2770,8 @@ void fpWalkOtns(int enabled, OtnWalkFcn fcn)
                 continue;
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 if ( enabled && (otn->rule_state != RULE_STATE_ENABLED) )
@@ -2732,7 +2808,8 @@ static int fpCreateServiceMaps(SnortConfig *sc)
             rtn = getRtnFromOtn(otn, policyId);
 
             if (rtn && ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP)))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP)))
             {
                 //do operation
 
@@ -2910,6 +2987,11 @@ static void fpCreateServiceMapPortGroups(SnortConfig *sc)
     fpBuildServicePortGroups(sc->spgmmTable->udp_to_cli, sc->sopgTable->udp_to_cli,
                              sc->srmmTable->udp_to_cli, fp);
 
+    fpBuildServicePortGroups(sc->spgmmTable->sctp_to_srv, sc->sopgTable->sctp_to_srv,
+                             sc->srmmTable->sctp_to_srv, fp);
+    fpBuildServicePortGroups(sc->spgmmTable->sctp_to_cli, sc->sopgTable->sctp_to_cli,
+                             sc->srmmTable->sctp_to_cli, fp);
+
     fpBuildServicePortGroups(sc->spgmmTable->icmp_to_srv, sc->sopgTable->icmp_to_srv,
                              sc->srmmTable->icmp_to_srv, fp);
     fpBuildServicePortGroups(sc->spgmmTable->icmp_to_cli, sc->sopgTable->icmp_to_cli,
@@ -2950,6 +3032,14 @@ PORT_GROUP * fpGetServicePortGroupByOrdinal(sopg_table_t *sopg, int proto, int d
 
            break;
 
+       case IPPROTO_SCTP:
+           if (dir == TO_SERVER)
+               pg = sopg->sctp_to_srv[proto_ordinal];
+           else
+               pg = sopg->sctp_to_cli[proto_ordinal];
+
+           break;
+
        case IPPROTO_ICMP:
            if (dir == TO_SERVER)
                pg = sopg->icmp_to_srv[proto_ordinal];
@@ -3029,6 +3119,9 @@ static void fpPrintServiceRuleMaps(srmm_table_t *service_map)
     fpPrintServiceRuleMapTable( service_map->udp_to_srv,  "udp to server" );
     fpPrintServiceRuleMapTable( service_map->udp_to_cli,  "udp to client" );
 
+    fpPrintServiceRuleMapTable( service_map->sctp_to_srv,  "sctp to server" );
+    fpPrintServiceRuleMapTable( service_map->sctp_to_cli,  "sctp to client" );
+
     fpPrintServiceRuleMapTable( service_map->icmp_to_srv, "icmp to server" );
     fpPrintServiceRuleMapTable( service_map->icmp_to_cli, "icmp to client" );
 
@@ -3056,6 +3149,11 @@ void fpPrintServicePortGroupSummary(srmm_table_t *srvc_pg_map)
     if(srvc_pg_map->udp_to_cli->count)
     LogMessage("| udp to cient   : %d services\n",srvc_pg_map->udp_to_cli->count);
 
+    if(srvc_pg_map->sctp_to_srv->count)
+    LogMessage("| sctp to server  : %d services\n",srvc_pg_map->sctp_to_srv->count);
+    if(srvc_pg_map->sctp_to_cli->count)
+    LogMessage("| sctp to cient   : %d services\n",srvc_pg_map->sctp_to_cli->count);
+
     if(srvc_pg_map->icmp_to_srv->count)
     LogMessage("| icmp to server : %d services\n",srvc_pg_map->icmp_to_srv->count);
     if(srvc_pg_map->icmp_to_cli->count)
diff --git a/src/fpcreate.h b/src/fpcreate.h
index 00817b9..cd7dbb9 100644
--- a/src/fpcreate.h
+++ b/src/fpcreate.h
@@ -45,7 +45,7 @@ struct _SnortConfig;
 
 /*
  *  Max Number of Protocols Supported by Rules in fpcreate.c
- *  for tcp,udp,icmp,ip ... this is an array dimesnion used to
+ *  for tcp,udp,sctp,icmp,ip ... this is an array dimesnion used to
  *  map protocol-ordinals to port_groups ...
  */
 /* This is now defined in sftarget_protocol_refererence.h"
@@ -116,6 +116,9 @@ typedef struct
   SFGHASH * udp_to_srv;
   SFGHASH * udp_to_cli;
 
+  SFGHASH * sctp_to_srv;
+  SFGHASH * sctp_to_cli;
+
   SFGHASH * icmp_to_srv;
   SFGHASH * icmp_to_cli;
 
@@ -135,6 +138,9 @@ typedef struct
   PORT_GROUP *udp_to_srv[MAX_PROTOCOL_ORDINAL];
   PORT_GROUP *udp_to_cli[MAX_PROTOCOL_ORDINAL];
 
+  PORT_GROUP *sctp_to_srv[MAX_PROTOCOL_ORDINAL];
+  PORT_GROUP *sctp_to_cli[MAX_PROTOCOL_ORDINAL];
+
   PORT_GROUP *icmp_to_srv[MAX_PROTOCOL_ORDINAL];
   PORT_GROUP *icmp_to_cli[MAX_PROTOCOL_ORDINAL];
 
@@ -167,6 +173,7 @@ void FastPatternConfigFree(FastPatternConfig *);
 */
 int prmFindRuleGroupTcp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupUdp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
+int prmFindRuleGroupSctp(PORT_RULE_MAP *, int, int, PORT_GROUP **, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupIp(PORT_RULE_MAP *, int, PORT_GROUP **, PORT_GROUP **);
 int prmFindRuleGroupIcmp(PORT_RULE_MAP *, int, PORT_GROUP **, PORT_GROUP **);
 
diff --git a/src/fpdetect.c b/src/fpdetect.c
index 72616ff..89f95aa 100644
--- a/src/fpdetect.c
+++ b/src/fpdetect.c
@@ -103,6 +103,7 @@ static inline int fpEvalHeaderIp(Packet *p, int ip_proto, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderIcmp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderTcp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderUdp(Packet *p, OTNX_MATCH_DATA *);
+static inline int fpEvalHeaderSctp(Packet *p, OTNX_MATCH_DATA *);
 static inline int fpEvalHeaderSW(PORT_GROUP *port_group, Packet *p,
                                  int check_ports, char ip_rule, OTNX_MATCH_DATA *);
 static int rule_tree_match (void* id, void * tree, int index, void * data, void *neg_list );
@@ -1032,6 +1033,7 @@ void printRuleFmt1( OptTreeNode * otn )
 
     if(      rtn->proto== IPPROTO_TCP     )LogMessage("tcp  ");
     else if( rtn->proto== IPPROTO_UDP     )LogMessage("udp  ");
+    else if( rtn->proto== IPPROTO_SCTP    )LogMessage("sctp ");
     else if( rtn->proto== IPPROTO_ICMP    )LogMessage("icmp ");
     else if( rtn->proto== ETHERNET_TYPE_IP)LogMessage("ip   ");
 
@@ -1064,7 +1066,7 @@ void printRuleFmt1( OptTreeNode * otn )
 **  FORMAL INPUTS
 **    PORT_GROUP * - the port group to inspect
 **    Packet *     - the packet to inspect
-**    int          - whether src/dst ports should be checked (udp/tcp or icmp)
+**    int          - whether src/dst ports should be checked (udp/tcp/sctp or icmp)
 **    char         - whether the rule is an IP rule (change the packet payload pointer)
 **
 **  FORMAL OUTPUTS
@@ -1533,6 +1535,90 @@ static inline int fpEvalHeaderTcp(Packet *p, OTNX_MATCH_DATA *omd)
 }
 
 /*
+**  fpEvalHeaderSctp::
+*/
+static inline int fpEvalHeaderSctp(Packet *p, OTNX_MATCH_DATA *omd)
+{
+    PORT_GROUP *src = NULL, *dst = NULL, *gen = NULL;
+
+#ifdef TARGET_BASED
+    if (IsAdaptiveConfigured(getRuntimePolicy(), 0))
+    {
+        int16_t proto_ordinal = GetProtocolReference(p);
+
+        DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "proto_ordinal=%d\n", proto_ordinal););
+
+        if (proto_ordinal > 0)
+        {
+            if (p->packet_flags & PKT_FROM_SERVER) /* to cli */
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "pkt_from_server\n"););
+
+                src = fpGetServicePortGroupByOrdinal(snort_conf->sopgTable, IPPROTO_SCTP,
+                                                     0 /*to_cli */,  proto_ordinal);
+            }
+
+            if (p->packet_flags & PKT_FROM_CLIENT) /* to srv */
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "pkt_from_client\n"););
+
+                dst = fpGetServicePortGroupByOrdinal(snort_conf->sopgTable, IPPROTO_SCTP,
+                                                     1 /*to_srv */,  proto_ordinal);
+            }
+
+            DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE,
+                        "fpEvalHeaderSctp:targetbased-ordinal-lookup: "
+                        "sport=%d, dport=%d, proto_ordinal=%d, src:%x, "
+                        "dst:%x, gen:%x\n",p->sp,p->dp,proto_ordinal,src,dst,gen););
+        }
+    }
+
+    if ((src == NULL) && (dst == NULL))
+    {
+        /* we did not have a target based group, use ports */
+        if (!prmFindRuleGroupSctp(snort_conf->prmSctpRTNX, p->dp, p->sp, &src, &dst, &gen))
+            return 0;
+
+        DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE,
+                    "fpEvalHeaderSctp: sport=%d, "
+                    "dport=%d, src:%x, dst:%x, gen:%x\n",p->sp,p->dp,src,dst,gen););
+    }
+#else
+    if (!prmFindRuleGroupSctp(snort_conf->prmSctpRTNX, p->dp, p->sp, &src, &dst, &gen))
+        return 0;
+#endif
+
+    if (fpDetectGetDebugPrintNcRules(snort_conf->fast_pattern_config))
+    {
+        LogMessage(
+            "fpEvalHeaderSctp: sport=%d, dport=%d, src:%p, dst:%p, gen:%p\n",
+             p->sp, p->dp, (void*)src, (void*)dst, (void*)gen);
+    }
+
+    InitMatchInfo(omd);
+
+    if (dst != NULL)
+    {
+        if (fpEvalHeaderSW(dst, p, 1, 0, omd))
+            return 1;
+    }
+
+    if (src != NULL)
+    {
+        if (fpEvalHeaderSW(src, p, 1, 0, omd))
+            return 1;
+    }
+
+    if (gen != NULL)
+    {
+        if(fpEvalHeaderSW(gen, p, 1, 0, omd))
+            return 1;
+    }
+
+    return fpFinalSelectEvent(omd, p);
+}
+
+/*
 **  fpEvalHeaderICMP::
 */
 static inline int fpEvalHeaderIcmp(Packet *p, OTNX_MATCH_DATA *omd)
@@ -1603,10 +1689,10 @@ static inline int fpEvalHeaderIp(Packet *p, int ip_proto, OTNX_MATCH_DATA *omd)
 **
 **  DESCRIPTION
 **    This function is the interface to the Detect() routine.  Here
-**    the IP protocol is processed.  If it is TCP, UDP, or ICMP, we
-**    process the both that particular ruleset and the IP ruleset
+**    the IP protocol is processed.  If it is TCP, UDP, SCTP, or ICMP,
+**    we process the both that particular ruleset and the IP ruleset
 **    with in the fpEvalHeader for that protocol.  If the protocol
-**    is not TCP, UDP, or ICMP, we just process the packet against
+**    is not TCP, UDP, SCTP, or ICMP, we just process the packet against
 **    the IP rules at the end of the fpEvalPacket routine.  Since
 **    we are using a setwise methodology for snort rules, both the
 **    network layer rules and the transport layer rules are done
@@ -1680,6 +1766,18 @@ int fpEvalPacket(Packet *p)
 
             return fpEvalHeaderUdp(p, omd);
 
+        case IPPROTO_SCTP:
+            DEBUG_WRAP(DebugMessage(DEBUG_DETECT,
+                        "Detecting on SctpList\n"););
+
+            if(p->sctph == NULL)
+            {
+                ip_proto = -1;
+                break;
+            }
+
+            return fpEvalHeaderSctp(p, omd);
+
         case IPPROTO_ICMPV6:
         case IPPROTO_ICMP:
             DEBUG_WRAP(DebugMessage(DEBUG_DETECT,
@@ -1698,7 +1796,7 @@ int fpEvalPacket(Packet *p)
     }
 
     /*
-    **  No Match on TCP/UDP, Do IP
+    **  No Match on TCP/UDP/SCTP, Do IP
     */
     return fpEvalHeaderIp(p, ip_proto, omd);
 }
diff --git a/src/generators.h b/src/generators.h
index 8c572c5..d02537c 100644
--- a/src/generators.h
+++ b/src/generators.h
@@ -222,6 +222,64 @@ enum {
     DECODE_INDEX_MAX
 };
 
+/* SCTP starts at 320 to leave room for other enums */
+#define     DECODE_SCTP_DGRAM_LT_SCTPHDR                320
+#define     DECODE_SCTP_CSUM_IS_ADLER32                 321
+#define     DECODE_SCTP_CHUNK_ZERO_LENGTH               322
+#define     DECODE_SCTP_CHUNK_LEN_GT_DGRAM              323
+#define     DECODE_SCTP_DATA_C_BAD_LEN                  324
+#define     DECODE_SCTP_DATA_C_RSVD_NZERO               325
+#define     DECODE_SCTP_INIT_C_BAD_LEN                  326
+#define     DECODE_SCTP_INIT_C_FLAGS_NZERO              327
+#define     DECODE_SCTP_INIT_C_ITAG_ZERO                328
+#define     DECODE_SCTP_INIT_C_VTAG_NZERO               329
+#define     DECODE_SCTP_INIT_C_NOUTSTR_ZERO             330
+#define     DECODE_SCTP_INIT_C_NINSTR_ZERO              331
+#define     DECODE_SCTP_INIT_ACK_C_BAD_LEN              332
+#define     DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO          333
+#define     DECODE_SCTP_INIT_ACK_C_ITAG_ZERO            334
+#define     DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO         335
+#define     DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO          336
+#define     DECODE_SCTP_SACK_C_BAD_LEN                  337
+#define     DECODE_SCTP_SACK_C_FLAGS_NZERO              338
+#define     DECODE_SCTP_HEARTBEAT_C_BAD_LEN             339
+#define     DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO         340
+#define     DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN         341
+#define     DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO     342
+#define     DECODE_SCTP_ABORT_C_BAD_LEN                 343
+#define     DECODE_SCTP_ABORT_C_RSVD_NZERO              344
+#define     DECODE_SCTP_SHUTDOWN_C_BAD_LEN              345
+#define     DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO          346
+#define     DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN          347
+#define     DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO      348
+#define     DECODE_SCTP_ERROR_C_BAD_LEN                 349
+#define     DECODE_SCTP_ERROR_C_FLAGS_NZERO             350
+#define     DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN           351
+#define     DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO       352
+#define     DECODE_SCTP_COOKIE_ACK_C_BAD_LEN            353
+#define     DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO        354
+#define     DECODE_SCTP_ECNE_C_BAD_LEN                  355
+#define     DECODE_SCTP_ECNE_C_FLAGS_NZERO              356
+#define     DECODE_SCTP_CWR_C_BAD_LEN                   357
+#define     DECODE_SCTP_CWR_C_FLAGS_NZERO               358
+#define     DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN     359
+#define     DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO  360
+#define     DECODE_SCTP_AUTH_C_BAD_LEN                  361
+#define     DECODE_SCTP_AUTH_C_FLAGS_NZERO              362
+#define     DECODE_SCTP_ASCONF_ACK_C_BAD_LEN            363
+#define     DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO        364
+#define     DECODE_SCTP_PKTDROP_C_BAD_LEN               365
+#define     DECODE_SCTP_PKTDROP_C_FLAGS_NZERO           366
+#define     DECODE_SCTP_PKTDROP_C_RSVD_NZERO            367
+#define     DECODE_SCTP_RECONFIG_C_BAD_LEN              368
+#define     DECODE_SCTP_RECONFIG_C_FLAGS_NZERO          369
+#define     DECODE_SCTP_PAD_C_BAD_LEN                   370
+#define     DECODE_SCTP_PAD_C_FLAGS_NZERO               371
+#define     DECODE_SCTP_FORWARD_TSN_C_BAD_LEN           372
+#define     DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO       373
+#define     DECODE_SCTP_ASCONF_C_BAD_LEN                374
+#define     DECODE_SCTP_ASCONF_C_FLAGS_NZERO            375
+
 
 //-----------------------------------------------------
 /*
@@ -319,6 +377,15 @@ enum {
 
 #define     PSNG_OPEN_PORT                         27
 
+#define     PSNG_SCTP_PORTSCAN                     28
+#define     PSNG_SCTP_DECOY_PORTSCAN               29
+#define     PSNG_SCTP_PORTSWEEP                    30
+#define     PSNG_SCTP_DISTRIBUTED_PORTSCAN         31
+#define     PSNG_SCTP_FILTERED_PORTSCAN            32
+#define     PSNG_SCTP_FILTERED_DECOY_PORTSCAN      33
+#define     PSNG_SCTP_PORTSWEEP_FILTERED           34
+#define     PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN     35
+
 #define GENERATOR_SPP_FRAG3                       123
 #define     FRAG3_IPOPTIONS                         1
 #define     FRAG3_TEARDROP                          2
@@ -723,6 +790,64 @@ enum {
 #define DECODE_IPV6_BAD_FRAG_PKT_STR "(snort_decoder) WARNING: bogus fragmentation packet. Possible BSD attack"
 #define DECODE_ZERO_LENGTH_FRAG_STR "(snort_decoder) WARNING: fragment with zero length"
 
+/* SCTP Decoder strings */
+#define DECODE_SCTP_DGRAM_LT_SCTPHDR_STR "(snort_decoder) WARNING: SCTP packet len is smaller than 12 bytes!"
+#define DECODE_SCTP_CSUM_IS_ADLER32_STR "snort_decoder) WARNING: SCTP Checksum is Adler32, not CRC32c!"
+#define DECODE_SCTP_CHUNK_ZERO_LENGTH_STR "(snort_decoder) WARNING: SCTP chunk length is zero bytes!"
+#define DECODE_SCTP_CHUNK_LEN_GT_DGRAM_STR "(snort_decoder) WARNING: SCTP chunk length is greater than remainder of the packet!"
+#define DECODE_SCTP_DATA_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP DATA chunk length is less than 17 bytes!"
+#define DECODE_SCTP_DATA_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP DATA chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_INIT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP INIT chunk length is less than 20 bytes!"
+#define DECODE_SCTP_INIT_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk flags field is non-zero!"
+#define DECODE_SCTP_INIT_C_ITAG_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk and initiate tag is == 0!"
+#define DECODE_SCTP_INIT_C_VTAG_NZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk and verification tag is > 0!"
+#define DECODE_SCTP_INIT_C_NOUTSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk # of outbound streams is zero!"
+#define DECODE_SCTP_INIT_C_NINSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT chunk # of inbound streams is zero!"
+#define DECODE_SCTP_INIT_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk length is less than 20 bytes!"
+#define DECODE_SCTP_INIT_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_INIT_ACK_C_ITAG_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk and initiate tag is == 0!"
+#define DECODE_SCTP_INIT_ACK_C_NOUTSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk # of outbound streams is zero!"
+#define DECODE_SCTP_INIT_ACK_C_NINSTR_ZERO_STR "(snort_decoder) WARNING: SCTP INIT_ACK chunk # of inbound streams is zero!"
+#define DECODE_SCTP_SACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SACK chunk length is less than 16 bytes!"
+#define DECODE_SCTP_SACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SACK chunk flags field is non-zero!"
+#define DECODE_SCTP_HEARTBEAT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP HEARTBEAT chunk length is less than 8 bytes!"
+#define DECODE_SCTP_HEARTBEAT_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP HEARTBEAT chunk flags field is non-zero!"
+#define DECODE_SCTP_HEARTBEAT_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP HEARTBEAT_ACK chunk length is less than 8 bytes!"
+#define DECODE_SCTP_HEARTBEAT_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP HEARTBEAT_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ABORT_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ABORT chunk length is less than 4 bytes!"
+#define DECODE_SCTP_ABORT_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP ABORT chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN chunk length is less than 8 bytes!"
+#define DECODE_SCTP_SHUTDOWN_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN chunk flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_ACK chunk length is less than 4 bytes!"
+#define DECODE_SCTP_SHUTDOWN_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ERROR_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ERROR chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ERROR_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ERROR chunk flags field is non-zero!"
+#define DECODE_SCTP_COOKIE_ECHO_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP COOKIE_ECHO chunk length is less than 4 bytes!"
+#define DECODE_SCTP_COOKIE_ECHO_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP COOKIE_ECHO chunk flags field is non-zero!"
+#define DECODE_SCTP_COOKIE_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP COOKIE_ACK chunk length is less than 4 bytes!"
+#define DECODE_SCTP_COOKIE_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP COOKIE_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_ECNE_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ECNE chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ECNE_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ECNE chunk flags field is non-zero!"
+#define DECODE_SCTP_CWR_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP CWR chunk length is less than 8 bytes!"
+#define DECODE_SCTP_CWR_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP CWR chunk flags field is non-zero!"
+#define DECODE_SCTP_SHUTDOWN_COMPLETE_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_COMPLETE chunk length is less than 4 bytes!"
+#define DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP SHUTDOWN_COMPLETE chunk reserved flags field is non-zero!"
+#define DECODE_SCTP_AUTH_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP AUTH chunk length is less than 4 bytes!"
+#define DECODE_SCTP_AUTH_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP AUTH chunk flags field is non-zero!"
+#define DECODE_SCTP_ASCONF_ACK_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ASCONF_ACK chunk length is less than 8 bytes!"
+#define DECODE_SCTP_ASCONF_ACK_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ASCONF_ACK chunk flags field is non-zero!"
+#define DECODE_SCTP_PKTDROP_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP PKTDROP chunk length is less than 8 bytes!"
+#define DECODE_SCTP_PKTDROP_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP PKTDROP chunk flags field is non-zero!"
+#define DECODE_SCTP_PKTDROP_C_RSVD_NZERO_STR "(snort_decoder) WARNING: SCTP PKTDROP reserved field is non-zero!"
+#define DECODE_SCTP_RECONFIG_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk length is less than 4 bytes!"
+#define DECODE_SCTP_RECONFIG_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP RECONFIG chunk flags field is non-zero!"
+#define DECODE_SCTP_PAD_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP PAD chunk length is less than 4 bytes!"
+#define DECODE_SCTP_PAD_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP PAD chunk flags field is non-zero!"
+#define DECODE_SCTP_FORWARD_TSN_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP FORWARD_TSN chunk length is less than 12 bytes!"
+#define DECODE_SCTP_FORWARD_TSN_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP FORWARD_TSN chunk flags field is non-zero!"
+#define DECODE_SCTP_ASCONF_C_BAD_LEN_STR "(snort_decoder) WARNING: SCTP ASCONF chunk length is less than 12 bytes!"
+#define DECODE_SCTP_ASCONF_C_FLAGS_NZERO_STR "(snort_decoder) WARNING: SCTP ASCONF chunk flags field is non-zero!"
+
 /*  RPC decode preprocessor strings */
 #define RPC_FRAG_TRAFFIC_STR "(spp_rpc_decode) Fragmented RPC Records"
 #define RPC_MULTIPLE_RECORD_STR "(spp_rpc_decode) Multiple RPC Records"
@@ -757,6 +882,15 @@ enum {
 #define PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN_STR "(portscan) UDP Filtered Distributed Portscan"
 #define PSNG_UDP_PORTSWEEP_FILTERED_STR "(portscan) UDP Filtered Portsweep"
 
+#define PSNG_SCTP_PORTSCAN_STR "(portscan) SCTP Portscan"
+#define PSNG_SCTP_DECOY_PORTSCAN_STR "(portscan) SCTP Decoy Portscan"
+#define PSNG_SCTP_PORTSWEEP_STR "(portscan) SCTP Portsweep"
+#define PSNG_SCTP_DISTRIBUTED_PORTSCAN_STR "(portscan) SCPT Distributed Portscan"
+#define PSNG_SCTP_FILTERED_PORTSCAN_STR "(portscan) SCTP Filtered Portscan"
+#define PSNG_SCTP_FILTERED_DECOY_PORTSCAN_STR "(portscan) SCPT Filtered Decoy Portscan"
+#define PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN_STR "(portscan) SCTP Filtered Distributed Portscan"
+#define PSNG_SCTP_PORTSWEEP_FILTERED_STR "(portscan) SCTP Filtered Portsweep"
+
 #define PSNG_ICMP_PORTSWEEP_STR "(portscan) ICMP Sweep"
 #define PSNG_ICMP_PORTSWEEP_FILTERED_STR "(portscan) ICMP Filtered Sweep"
 
diff --git a/src/parser.c b/src/parser.c
index 159ffd4..f2804cf 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -133,6 +133,7 @@
 #define RULE_PROTO_OPT__IP    "ip"
 #define RULE_PROTO_OPT__TCP   "tcp"
 #define RULE_PROTO_OPT__UDP   "udp"
+#define RULE_PROTO_OPT__SCTP  "sctp"
 #define RULE_PROTO_OPT__ICMP  "icmp"
 
 #define RULE_DIR_OPT__DIRECTIONAL    "->"
@@ -258,6 +259,8 @@
 #define CHECKSUM_MODE_OPT__NO_TCP   "notcp"
 #define CHECKSUM_MODE_OPT__UDP      "udp"
 #define CHECKSUM_MODE_OPT__NO_UDP   "noudp"
+#define CHECKSUM_MODE_OPT__SCTP     "sctp"
+#define CHECKSUM_MODE_OPT__NO_SCTP  "nosctp"
 #define CHECKSUM_MODE_OPT__ICMP     "icmp"
 #define CHECKSUM_MODE_OPT__NO_ICMP  "noicmp"
 
@@ -463,6 +466,7 @@ static port_list_t port_list;
 
 static rule_count_t tcpCnt;
 static rule_count_t udpCnt;
+static rule_count_t sctpCnt;
 static rule_count_t icmpCnt;
 static rule_count_t ipCnt;
 
@@ -804,7 +808,7 @@ static void PortTablesFinish(rule_port_tables_t *, FastPatternConfig *);
 static rule_port_tables_t * PortTablesNew(void);
 static int FinishPortListRule(rule_port_tables_t *, RuleTreeNode *, OptTreeNode *,
                               int, port_entry_t *, FastPatternConfig *);
-static PortObject * ParsePortListTcpUdpPort(PortVarTable *, PortTable *, char *);
+static PortObject * ParsePortListProtosWithPorts(PortVarTable *, PortTable *, char *);
 static int ParsePortList(RuleTreeNode *, PortVarTable *, PortTable *, char *, int, int);
 static void ParseRule(SnortConfig *, SnortPolicy *, char *, RuleType, ListHead *);
 static void TransferOutputConfigs(OutputConfig *, OutputConfig **);
@@ -1210,6 +1214,7 @@ static void DumpRuleChains(RuleListNode *rule_lists)
         DumpChain(rule->name, rule->mode, ETHERNET_TYPE_IP);
         DumpChain(rule->name, rule->mode, IPPROTO_TCP);
         DumpChain(rule->name, rule->mode, IPPROTO_UDP);
+        DumpChain(rule->name, rule->mode, IPPROTO_SCTP);
         DumpChain(rule->name, rule->mode, IPPROTO_ICMP);
 
         rule = rule->next;
@@ -1277,7 +1282,7 @@ static void DumpList(IpAddrNode *idx, int negated)
 /*
  * Finish adding the rule to the port tables
  *
- * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
+ * 1) find the table this rule should belong to (src/dst/any-any tcp,udp,sctp,icmp,ip or nocontent)
  * 2) find an index for the sid:gid pair
  * 3) add all no content rules to a single no content port object, the ports are irrelevant so
  *    make it a any-any port object.
@@ -1327,6 +1332,14 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         ncObject = port_tables->icmp_nocontent;
         prc = &icmpCnt;
     }
+    else if (proto == IPPROTO_SCTP)
+    {
+        dstTable = port_tables->sctp_dst;
+        srcTable = port_tables->sctp_src;
+        aaObject = port_tables->sctp_anyany;
+        ncObject = port_tables->sctp_nocontent;
+        prc = &sctpCnt;
+    }
     else if (proto == ETHERNET_TYPE_IP)
     {
         dstTable = port_tables->ip_dst;
@@ -1409,8 +1422,8 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         {
             /* Add the IP rules to the higher level app protocol groups, if they apply
              * to those protocols.  All IP rules should have any-any port descriptors
-             * and fall into this test.  IP rules that are not tcp/udp/icmp go only into the
-             * IP table */
+             * and fall into this test.  IP rules that are not tcp/udp/sctp/icmp go only
+             * into the IP table */
             DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,
                                     "Finishing IP any-any rule %u:%u\n",
                                     otn->sigInfo.generator,otn->sigInfo.id););
@@ -1432,6 +1445,11 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
                     icmpCnt.aa++;
                     break;
 
+                case IPPROTO_SCTP:
+                    PortObjectAddRule(port_tables->sctp_anyany, rim_index);
+                    sctpCnt.aa++;
+                    break;
+
                 case -1:  /* Add to all ip proto anyany port tables */
                     PortObjectAddRule(port_tables->tcp_anyany, rim_index);
                     tcpCnt.aa++;
@@ -1439,6 +1457,9 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
                     PortObjectAddRule(port_tables->udp_anyany, rim_index);
                     udpCnt.aa++;
 
+                    PortObjectAddRule(port_tables->sctp_anyany, rim_index);
+                    sctpCnt.aa++;
+
                     PortObjectAddRule(port_tables->icmp_anyany, rim_index);
                     icmpCnt.aa++;
 
@@ -1454,7 +1475,7 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
         }
         else
         {
-            /* For other protocols-tcp/udp/icmp add to the any any group */
+            /* For other protocols-tcp/udp/sctp/icmp add to the any any group */
             PortObjectAddRule(aaObject, rim_index);
             prc->aa++;
         }
@@ -1560,8 +1581,8 @@ static int FinishPortListRule(rule_port_tables_t *port_tables, RuleTreeNode *rtn
 *  to build PORT_GROUP objects. Those are generated after the otn processing.
 *
 */
-static PortObject * ParsePortListTcpUdpPort(PortVarTable *pvt,
-                                            PortTable *noname, char *port_str)
+static PortObject * ParsePortListProtosWithPorts(PortVarTable *pvt,
+                                                PortTable *noname, char *port_str)
 {
     PortObject * portobject;
     //PortObject * pox;
@@ -1710,18 +1731,18 @@ int GetOtnIcmpType(OptTreeNode * otn )
  *   Process the rule, add it to the appropriate PortObject
  *   and add the PortObject to the rtn.
  *
- *   TCP/UDP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
+ *   TCP/UDP/SCTP rules use ports/portlists, icmp uses the icmp type field and ip uses the protocol
  *   field as a dst port for the purposes of looking up a rule group as packets are being
  *   processed.
  *
- *   TCP/UDP- use src/dst ports
- *   ICMP   - use icmp type as dst port,src=-1
- *   IP     - use protocol as dst port,src=-1
+ *   TCP/UDP/SCTP - use src/dst ports
+ *   ICMP         - use icmp type as dst port, src=-1
+ *   IP           - use protocol as dst port, src=-1
  *
- *   rtn - proto_node
- *   port_str - port list string or port var name
- *   proto - protocol
- *   dst_flag - dst or src port flag, true = dst, false = src
+ *   rtn          - proto_node
+ *   port_str     - port list string or port var name
+ *   proto        - protocol
+ *   dst_flag     - dst or src port flag, true = dst, false = src
  *
  */
 static int ParsePortList(RuleTreeNode *rtn, PortVarTable *pvt, PortTable *noname,
@@ -1730,9 +1751,9 @@ static int ParsePortList(RuleTreeNode *rtn, PortVarTable *pvt, PortTable *noname
     PortObject *portobject = NULL;  /* src or dst */
 
     /* Get the protocol specific port object */
-    if( proto == IPPROTO_TCP || proto == IPPROTO_UDP )
+    if( proto == IPPROTO_TCP || proto == IPPROTO_UDP || proto == IPPROTO_SCTP )
     {
-        portobject = ParsePortListTcpUdpPort(pvt, noname, port_str);
+        portobject = ParsePortListProtosWithPorts(pvt, noname, port_str);
     }
     else /* ICMP, IP  - no real ports just Type and Protocol */
     {
@@ -3176,6 +3197,10 @@ static int GetRuleProtocol(char *proto_str)
     {
         return IPPROTO_UDP;
     }
+    else if (strcasecmp(proto_str, RULE_PROTO_OPT__SCTP) == 0)
+    {
+        return IPPROTO_SCTP;
+    }
     else if (strcasecmp(proto_str, RULE_PROTO_OPT__ICMP) == 0)
     {
         return IPPROTO_ICMP;
@@ -4360,6 +4385,7 @@ static void LinkDynamicRules(SnortConfig *sc)
 
     SetLinks(sc, IPPROTO_TCP);
     SetLinks(sc, IPPROTO_UDP);
+    SetLinks(sc, IPPROTO_SCTP);
     SetLinks(sc, IPPROTO_ICMP);
 }
 
@@ -4662,7 +4688,8 @@ int CheckRuleStates(SnortConfig *sc)
             }
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP) ||
-                (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                (rtn->proto == IPPROTO_ICMP) || (rtn->proto == IPPROTO_SCTP) ||
+                (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 if ( otn->sigInfo.shared )
@@ -4765,7 +4792,7 @@ void SetRuleStates(SnortConfig *sc)
         otn->rule_state = rule_state->state;
     }
 
-    /* Check TCP/UDP/ICMP/IP in one iteration for all rulelists and for all policies*/
+    /* Check TCP/UDP/SCTP/ICMP/IP in one iteration for all rulelists and for all policies*/
     err = CheckRuleStates(sc);
     if (err)
         oneErr = 1;
@@ -5697,6 +5724,7 @@ static void InitParser(void)
 
     memset(&tcpCnt, 0, sizeof(tcpCnt));
     memset(&udpCnt, 0, sizeof(udpCnt));
+    memset(&sctpCnt, 0, sizeof(sctpCnt));
     memset(&ipCnt, 0, sizeof(ipCnt));
     memset(&icmpCnt, 0, sizeof(icmpCnt));
 
@@ -5787,12 +5815,12 @@ void ParseRules(SnortConfig *sc)
     PortTablesFinish(sc->port_tables, sc->fast_pattern_config);
 
     LogMessage("+-------------------[Rule Port Counts]---------------------------------------\n");
-    LogMessage("|%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "icmp", "ip");
-    LogMessage("|%8s%8u%8u%8u%8u\n", "src", tcpCnt.src, udpCnt.src, icmpCnt.src, ipCnt.src);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "dst", tcpCnt.dst, udpCnt.dst, icmpCnt.dst, ipCnt.dst);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "any", tcpCnt.aa, udpCnt.aa, icmpCnt.aa, ipCnt.aa);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "nc", tcpCnt.nc, udpCnt.nc, icmpCnt.nc, ipCnt.nc);
-    LogMessage("|%8s%8u%8u%8u%8u\n", "s+d", tcpCnt.sd, udpCnt.sd, icmpCnt.sd, ipCnt.sd);
+    LogMessage("|%8s%8s%8s%8s%8s%8s\n", " ", "tcp", "udp", "sctp", "icmp", "ip");
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "src", tcpCnt.src, udpCnt.src, sctpCnt.src, icmpCnt.src, ipCnt.src);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "dst", tcpCnt.dst, udpCnt.dst, sctpCnt.dst, icmpCnt.dst, ipCnt.dst);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "any", tcpCnt.aa, udpCnt.aa, sctpCnt.aa, icmpCnt.aa, ipCnt.aa);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "nc", tcpCnt.nc, udpCnt.nc, sctpCnt.nc, icmpCnt.nc, ipCnt.nc);
+    LogMessage("|%8s%8u%8u%8u%8u%8u\n", "s+d", tcpCnt.sd, udpCnt.sd, sctpCnt.sd, icmpCnt.sd, ipCnt.sd);
     LogMessage("+----------------------------------------------------------------------------\n");
 
     ///print_rule_index_map( ruleIndexMap );
@@ -6343,6 +6371,18 @@ static int GetChecksumFlags(char *args)
             negative_flags |= CHECKSUM_FLAG__UDP;
             got_negative_flag = 1;
         }
+        else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__SCTP) == 0)
+        {
+            positive_flags |= CHECKSUM_FLAG__SCTP;
+            negative_flags &= ~CHECKSUM_FLAG__SCTP;
+            got_positive_flag = 1;
+        }
+        else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__NO_SCTP) == 0)
+        {
+            positive_flags &= ~CHECKSUM_FLAG__SCTP;
+            negative_flags |= CHECKSUM_FLAG__SCTP;
+            got_negative_flag = 1;
+        }
         else if (strcasecmp(toks[i], CHECKSUM_MODE_OPT__ICMP) == 0)
         {
             positive_flags |= CHECKSUM_FLAG__ICMP;
@@ -7365,7 +7405,7 @@ void ConfigFlowbitsSize(SnortConfig *sc, char *args)
 /****************************************************************************
  *
  * Purpose: Parses a protocol plus a list of ports.
- *          The protocol should be "udp" or "tcp".
+ *          The protocol should be "tcp", "udp", or "sctp".
  *          The ports list should be a list of numbers or pairs of numbers.
  *          Each element of the list is separated by a space character.
  *          Each pair of numbers is separated by a colon character.
@@ -7402,7 +7442,7 @@ void ConfigIgnorePorts(SnortConfig *sc, char *args)
 
     protocol = GetRuleProtocol(toks[0]);
 
-    if ( !(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP) )
+    if ( !(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP || protocol == IPPROTO_SCTP) )
     {
         ParseError("Invalid protocol: %s.", toks[0]);
     }
@@ -7414,10 +7454,20 @@ void ConfigIgnorePorts(SnortConfig *sc, char *args)
 
         for ( p = lo_port; p <= hi_port; p++ )
         {
-            if (protocol == IPPROTO_TCP)
-                sc->ignore_ports[p] |= PROTO_BIT__TCP;
-            else if (protocol == IPPROTO_UDP)
-                sc->ignore_ports[p] |= PROTO_BIT__UDP;
+            switch (protocol)
+            {
+                case IPPROTO_TCP:
+                    sc->ignore_ports[p] |= PROTO_BIT__TCP;
+                    break;
+
+                case IPPROTO_UDP:
+                    sc->ignore_ports[p] |= PROTO_BIT__UDP;
+                    break;
+
+                case IPPROTO_SCTP:
+                    sc->ignore_ports[p] |= PROTO_BIT__SCTP;
+                    break;
+            }
         }
     }
 
@@ -9146,6 +9196,9 @@ static void ParseRule(SnortConfig *sc, SnortPolicy *p, char *args,
             case IPPROTO_UDP:
                 sc->ip_proto_array[IPPROTO_UDP] = 1;
                 break;
+            case IPPROTO_SCTP:
+                sc->ip_proto_array[IPPROTO_SCTP] = 1;
+                break;
             case IPPROTO_ICMP:
                 sc->ip_proto_array[IPPROTO_ICMP] = 1;
                 sc->ip_proto_array[IPPROTO_ICMPV6] = 1;
@@ -11156,22 +11209,27 @@ static rule_port_tables_t * PortTablesNew(void)
     /* No content rule objects */
     rpt->tcp_nocontent = PortObjectNew();
     if (rpt->tcp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile tcp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->tcp_nocontent);
 
     rpt->udp_nocontent = PortObjectNew();
     if (rpt->udp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile udp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->udp_nocontent);
 
+    rpt->sctp_nocontent = PortObjectNew();
+    if (rpt->sctp_nocontent == NULL)
+        FatalError("ParseRulesFile sctp nocontent PortObjectNew() failed\n");
+    PortObjectAddPortAny(rpt->sctp_nocontent);
+
     rpt->icmp_nocontent = PortObjectNew();
     if (rpt->icmp_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile icmp nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->icmp_nocontent);
 
     rpt->ip_nocontent = PortObjectNew();
     if (rpt->ip_nocontent == NULL)
-        ParseError("ParseRulesFile nocontent PortObjectNew() failed\n");
+        ParseError("ParseRulesFile ip nocontent PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->ip_nocontent);
 
     /* Create the Any-Any Port Objects for each protocol */
@@ -11185,6 +11243,11 @@ static rule_port_tables_t * PortTablesNew(void)
         ParseError("ParseRulesFile udp any-any PortObjectNew() failed\n");
     PortObjectAddPortAny(rpt->udp_anyany);
 
+    rpt->sctp_anyany = PortObjectNew();
+    if (rpt->sctp_anyany == NULL)
+        FatalError("ParseRulesFile sctp any-any PortObjectNew() failed\n");
+    PortObjectAddPortAny(rpt->sctp_anyany);
+
     rpt->icmp_anyany = PortObjectNew();
     if (rpt->icmp_anyany == NULL)
         ParseError("ParseRulesFile icmp any-any PortObjectNew() failed\n");
@@ -11213,6 +11276,15 @@ static rule_port_tables_t * PortTablesNew(void)
     if (rpt->udp_dst == NULL)
         ParseError("ParseRulesFile udp-dst PortTableNew() failed\n");
 
+    /* Create the sctp Rules PortTables */
+    rpt->sctp_src = PortTableNew();
+    if (rpt->sctp_src == NULL)
+        FatalError("ParseRulesFile sctp-src PortTableNew() failed\n");
+
+    rpt->sctp_dst = PortTableNew();
+    if (rpt->sctp_dst == NULL)
+        FatalError("ParseRulesFile sctp-dst PortTableNew() failed\n");
+
     /* Create the icmp Rules PortTables */
     rpt->icmp_src = PortTableNew();
     if (rpt->icmp_src == NULL)
@@ -11239,6 +11311,8 @@ static rule_port_tables_t * PortTablesNew(void)
     rpt->tcp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->udp_src->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->udp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
+    rpt->sctp_src->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
+    rpt->sctp_dst->pt_lrc = DEFAULT_LARGE_RULE_GROUP;
     rpt->icmp_src->pt_lrc= DEFAULT_LARGE_RULE_GROUP;
     rpt->icmp_dst->pt_lrc= DEFAULT_LARGE_RULE_GROUP;
     rpt->ip_src->pt_lrc  = DEFAULT_LARGE_RULE_GROUP;
@@ -11271,6 +11345,17 @@ static void PortTablesFinish(rule_port_tables_t *port_tables, FastPatternConfig
     finish_portlist_table(fp, "udp src", port_tables->udp_src);
     finish_portlist_table(fp, "udp dst", port_tables->udp_dst);
 
+    /* SCTP-SRC */
+    if (fpDetectGetDebugPrintRuleGroupsCompiled(fp))
+    {
+        LogMessage("*** SCTP-Any-Any Port List\n");
+        PortObjectPrintEx(port_tables->sctp_anyany,
+                          rule_index_map_print_index);
+    }
+
+    finish_portlist_table(fp, "sctp src", port_tables->sctp_src);
+    finish_portlist_table(fp, "sctp dst", port_tables->sctp_dst);
+
     /* ICMP-SRC */
     if (fpDetectGetDebugPrintRuleGroupsCompiled(fp))
     {
@@ -11295,10 +11380,12 @@ static void PortTablesFinish(rule_port_tables_t *port_tables, FastPatternConfig
 
     RuleListSortUniq(port_tables->tcp_anyany->rule_list);
     RuleListSortUniq(port_tables->udp_anyany->rule_list);
+    RuleListSortUniq(port_tables->sctp_anyany->rule_list);
     RuleListSortUniq(port_tables->icmp_anyany->rule_list);
     RuleListSortUniq(port_tables->ip_anyany->rule_list);
     RuleListSortUniq(port_tables->tcp_nocontent->rule_list);
     RuleListSortUniq(port_tables->udp_nocontent->rule_list);
+    RuleListSortUniq(port_tables->sctp_nocontent->rule_list);
     RuleListSortUniq(port_tables->icmp_nocontent->rule_list);
     RuleListSortUniq(port_tables->ip_nocontent->rule_list);
 }
@@ -11356,6 +11443,10 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortTableFree(port_tables->udp_src);
     if (port_tables->udp_dst)
         PortTableFree(port_tables->udp_dst);
+    if (port_tables->sctp_src)
+        PortTableFree(port_tables->sctp_src);
+    if (port_tables->sctp_dst)
+        PortTableFree(port_tables->sctp_dst);
     if (port_tables->icmp_src)
         PortTableFree(port_tables->icmp_src);
     if (port_tables->icmp_dst)
@@ -11369,6 +11460,8 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortObjectFree(port_tables->tcp_anyany);
     if (port_tables->udp_anyany)
         PortObjectFree(port_tables->udp_anyany);
+    if (port_tables->sctp_anyany)
+        PortObjectFree(port_tables->sctp_anyany);
     if (port_tables->icmp_anyany)
         PortObjectFree(port_tables->icmp_anyany);
     if (port_tables->ip_anyany)
@@ -11378,6 +11471,8 @@ void PortTablesFree(rule_port_tables_t *port_tables)
         PortObjectFree(port_tables->tcp_nocontent);
     if (port_tables->udp_nocontent)
         PortObjectFree(port_tables->udp_nocontent);
+    if (port_tables->sctp_nocontent)
+        PortObjectFree(port_tables->sctp_nocontent);
     if (port_tables->icmp_nocontent)
         PortObjectFree(port_tables->icmp_nocontent);
     if (port_tables->ip_nocontent)
@@ -11912,7 +12007,8 @@ static void IntegrityCheckRules(SnortConfig *sc)
             }
 
             if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP)
-                    || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP))
+                    || (rtn->proto == IPPROTO_SCTP) || (rtn->proto == IPPROTO_ICMP)
+                    || (rtn->proto == ETHERNET_TYPE_IP))
             {
                 //do operation
                 ofl_idx = otn->opt_func;
diff --git a/src/plugbase.c b/src/plugbase.c
index 836eb09..ddad8b0 100644
--- a/src/plugbase.c
+++ b/src/plugbase.c
@@ -94,6 +94,11 @@
 #include "detection-plugins/sp_base64_data.h"
 #include "detection-plugins/sp_pkt_data.h"
 #include "detection-plugins/sp_asn1.h"
+#include "detection-plugins/sp_sctp_chunk_type.h"
+#include "detection-plugins/sp_sctp_chunk_flags.h"
+#include "detection-plugins/sp_sctp_chunk_field.h"
+#include "detection-plugins/sp_sctp_num_chunks.h"
+#include "detection-plugins/sp_sctp_cause_code.h"
 #ifdef ENABLE_REACT
 #include "detection-plugins/sp_react.h"
 #endif
@@ -203,6 +208,11 @@ void RegisterRuleOptions(void)
     SetupFTPBounce();
     SetupUriLenCheck();
     SetupCvs();
+    SctpChunkTypeSetup();
+    SctpChunkFlagsSetup();
+    SctpChunkFieldSetup();
+    SctpNumChunksSetup();
+    SctpCauseCodeSetup();
 }
 
 /****************************************************************************
@@ -386,7 +396,7 @@ void DumpRuleOptions(void)
 
     while (node != NULL)
     {
-        LogMessage("%-13s:      %p\n", node->keyword, (void *)node->vfunc);
+        LogMessage("%-16s:      %p\n", node->keyword, (void *)node->vfunc);
         node = node->next;
     }
 
@@ -832,7 +842,7 @@ void DumpPreprocessors(void)
 
     while (node != NULL)
     {
-        LogMessage("%-13s:       %p\n", node->keyword, node->config_vfunc);
+        LogMessage("%-20s:       %p\n", node->keyword, node->config_vfunc);
         node = node->next;
     }
 
@@ -1733,7 +1743,7 @@ void DumpOutputPlugins(void)
     LogMessage("-------------------------------------------------\n");
     while(idx != NULL)
     {
-        LogMessage("%-13s:       %p\n", idx->keyword, idx->config_vfunc);
+        LogMessage("%-19s:       %p\n", idx->keyword, idx->config_vfunc);
         idx = idx->next;
     }
     LogMessage("-------------------------------------------------\n\n");
diff --git a/src/plugin_enum.h b/src/plugin_enum.h
index edc75a8..668419b 100644
--- a/src/plugin_enum.h
+++ b/src/plugin_enum.h
@@ -62,6 +62,10 @@ enum {
     PLUGIN_FLOWBIT,
     PLUGIN_FILE_DATA,
     PLUGIN_BASE64_DECODE,
+    PLUGIN_SCTP_CHUNK_TYPE,
+    PLUGIN_SCTP_CHUNK_FLAGS,
+    PLUGIN_SCTP_NUM_CHUNKS,
+    PLUGIN_SCTP_CAUSE_CODE,
     PLUGIN_MAX  /* sentinel value */
 };
 
diff --git a/src/preprocessors/Stream5/Makefile.am b/src/preprocessors/Stream5/Makefile.am
index 5026540..015b315 100644
--- a/src/preprocessors/Stream5/Makefile.am
+++ b/src/preprocessors/Stream5/Makefile.am
@@ -8,6 +8,8 @@ snort_stream5_tcp.c \
 snort_stream5_tcp.h \
 snort_stream5_udp.c \
 snort_stream5_udp.h \
+snort_stream5_sctp.c \
+snort_stream5_sctp.h \
 snort_stream5_icmp.c \
 snort_stream5_icmp.h \
 snort_stream5_ip.c \
@@ -22,6 +24,7 @@ stream5_common.h
 libstream5_a_LIBADD = \
 snort_stream5_tcp.o \
 snort_stream5_udp.o \
+snort_stream5_sctp.o \
 snort_stream5_icmp.o \
 snort_stream5_ip.o \
 snort_stream5_session.o \
diff --git a/src/preprocessors/Stream5/Makefile.in b/src/preprocessors/Stream5/Makefile.in
index 371a126..4beba53 100644
--- a/src/preprocessors/Stream5/Makefile.in
+++ b/src/preprocessors/Stream5/Makefile.in
@@ -65,11 +65,12 @@ LIBRARIES = $(noinst_LIBRARIES)
 ARFLAGS = cru
 libstream5_a_AR = $(AR) $(ARFLAGS)
 libstream5_a_DEPENDENCIES = snort_stream5_tcp.o snort_stream5_udp.o \
-	snort_stream5_icmp.o snort_stream5_ip.o \
+	snort_stream5_sctp.o snort_stream5_icmp.o snort_stream5_ip.o \
 	snort_stream5_session.o stream5_paf.o stream5_common.o
 am_libstream5_a_OBJECTS = snort_stream5_tcp.$(OBJEXT) \
-	snort_stream5_udp.$(OBJEXT) snort_stream5_icmp.$(OBJEXT) \
-	snort_stream5_ip.$(OBJEXT) snort_stream5_session.$(OBJEXT) \
+	snort_stream5_udp.$(OBJEXT) snort_stream5_sctp.$(OBJEXT) \
+	snort_stream5_icmp.$(OBJEXT) snort_stream5_ip.$(OBJEXT) \
+	snort_stream5_session.$(OBJEXT) \
 	stream5_paf.$(OBJEXT) stream5_common.$(OBJEXT)
 libstream5_a_OBJECTS = $(am_libstream5_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
@@ -232,6 +233,8 @@ snort_stream5_tcp.c \
 snort_stream5_tcp.h \
 snort_stream5_udp.c \
 snort_stream5_udp.h \
+snort_stream5_sctp.c \
+snort_stream5_sctp.h \
 snort_stream5_icmp.c \
 snort_stream5_icmp.h \
 snort_stream5_ip.c \
@@ -246,6 +249,7 @@ stream5_common.h
 libstream5_a_LIBADD = \
 snort_stream5_tcp.o \
 snort_stream5_udp.o \
+snort_stream5_sctp.o \
 snort_stream5_icmp.o \
 snort_stream5_ip.o \
 snort_stream5_session.o \
diff --git a/src/preprocessors/Stream5/snort_stream5_sctp.c b/src/preprocessors/Stream5/snort_stream5_sctp.c
new file mode 100644
index 0000000..70f76b3
--- /dev/null
+++ b/src/preprocessors/Stream5/snort_stream5_sctp.c
@@ -0,0 +1,792 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sf_types.h"
+#include "snort_debug.h"
+#include "detect.h"
+#include "plugbase.h"
+#include "mstring.h"
+#include "sfxhash.h"
+#include "util.h"
+#include "decode.h"
+
+#include "stream5_common.h"
+#include "stream_api.h"
+#include "snort_stream5_session.h"
+#include "stream_expect.h"
+#include "snort_stream5_sctp.h"
+
+#include "plugin_enum.h"
+#include "rules.h"
+#include "treenodes.h"
+#include "snort.h"
+#include "active.h"
+
+#include "portscan.h" /* To know when to create sessions for all SCTP */
+
+#include "dynamic-plugins/sp_dynamic.h"
+
+#include "profiler.h"
+#include "sfPolicy.h"
+
+#ifdef PERF_PROFILING
+PreprocStats s5SctpPerfStats;
+#endif
+
+/*  M A C R O S  **************************************************/
+/* actions */
+#define ACTION_NOTHING                  0x00000000
+
+/* sender/responder ip/port dereference */
+#define sctp_sender_ip lwSsn->client_ip
+#define sctp_sender_port lwSsn->client_port
+#define sctp_responder_ip lwSsn->server_ip
+#define sctp_responder_port lwSsn->server_port
+
+/*  D A T A  S T R U C T U R E S  ***********************************/
+typedef struct _SctpSession
+{
+    Stream5LWSession *lwSsn;
+
+    struct timeval ssn_time;
+
+    //uint8_t    c_ttl;
+    //uint8_t    s_ttl;
+
+} SctpSession;
+
+
+/*  G L O B A L S  **************************************************/
+Stream5SessionCache *sctp_lws_cache = NULL;
+static MemPool sctp_session_mempool;
+
+/*  P R O T O T Y P E S  ********************************************/
+static void Stream5ParseSctpArgs(Stream5SctpConfig *, char *, Stream5SctpPolicy *);
+static void Stream5PrintSctpConfig(Stream5SctpPolicy *);
+void SctpSessionCleanup(Stream5LWSession *lwssn);
+static int ProcessSctp(Stream5LWSession *, Packet *, Stream5SctpPolicy *, SFXHASH_NODE *);
+
+void Stream5InitSctp(Stream5GlobalConfig *gconfig)
+{
+    if (gconfig == NULL)
+        return;
+
+    /* Now SCTP */
+    if ((sctp_lws_cache == NULL) && (gconfig->track_sctp_sessions))
+    {
+        sctp_lws_cache = InitLWSessionCache(gconfig->max_sctp_sessions,
+                                           30, (3*60), 5, 0, &SctpSessionCleanup);
+
+        if(!sctp_lws_cache)
+        {
+            FatalError("Unable to init stream5 SCTP session cache, no SCTP "
+                       "stream inspection!\n");
+        }
+
+        if (mempool_init(&sctp_session_mempool,
+                    gconfig->max_sctp_sessions, sizeof(SctpSession)) != 0)
+        {
+            FatalError("%s(%d) Could not initialize sctp session memory pool.\n",
+                    __FILE__, __LINE__);
+        }
+    }
+}
+
+void Stream5SctpPolicyInit(Stream5SctpConfig *config, char *args)
+{
+    Stream5SctpPolicy *s5SctpPolicy;
+
+    if (config == NULL)
+        return;
+
+    s5SctpPolicy = (Stream5SctpPolicy *)SnortAlloc(sizeof(Stream5SctpPolicy));
+
+    Stream5ParseSctpArgs(config, args, s5SctpPolicy);
+
+    config->num_policies++;
+
+    /* Now add this context to the internal list */
+    if (config->policy_list == NULL)
+    {
+        config->policy_list =
+            (Stream5SctpPolicy **)SnortAlloc(sizeof(Stream5SctpPolicy *));
+    }
+    else
+    {
+        Stream5SctpPolicy **tmpPolicyList =
+            (Stream5SctpPolicy **)SnortAlloc(sizeof(Stream5SctpPolicy *) * config->num_policies);
+
+        memcpy(tmpPolicyList, config->policy_list,
+               sizeof(Stream5SctpPolicy *) * (config->num_policies - 1));
+
+        free(config->policy_list);
+
+        config->policy_list = tmpPolicyList;
+    }
+
+    config->policy_list[config->num_policies - 1] = s5SctpPolicy;
+
+    Stream5PrintSctpConfig(s5SctpPolicy);
+
+#ifdef REG_TEST
+    LogMessage("\n");
+    LogMessage("    SCTP Session Size: %lu\n",sizeof(SctpSession));
+    LogMessage("\n");
+#endif
+}
+
+static void Stream5ParseSctpArgs(Stream5SctpConfig *config, char *args, Stream5SctpPolicy *s5SctpPolicy)
+{
+    char **toks;
+    int num_toks;
+    int i;
+    char *index;
+    char **stoks = NULL;
+    int s_toks;
+    char *endPtr = NULL;
+
+    if (s5SctpPolicy == NULL)
+        return;
+
+    s5SctpPolicy->session_timeout = S5_DEFAULT_SSN_TIMEOUT;
+    s5SctpPolicy->flags = 0;
+
+    if(args != NULL && strlen(args) != 0)
+    {
+        toks = mSplit(args, ",", 6, &num_toks, 0);
+
+        i=0;
+
+        while(i < num_toks)
+        {
+            index = toks[i];
+
+            while(isspace((int)*index)) index++;
+
+            stoks = mSplit(index, " ", 3, &s_toks, 0);
+
+            if (s_toks == 0)
+            {
+                FatalError("%s(%d) => Missing parameter in Stream5 SCTP config.\n",
+                    file_name, file_line);
+            }
+
+            if(!strcasecmp(stoks[0], "timeout"))
+            {
+                if(stoks[1])
+                {
+                    s5SctpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10);
+                }
+
+                if (!stoks[1] || (endPtr == &stoks[1][0]))
+                {
+                    FatalError("%s(%d) => Invalid timeout in config file.  Integer parameter required.\n",
+                            file_name, file_line);
+                }
+
+                if ((s5SctpPolicy->session_timeout > S5_MAX_SSN_TIMEOUT) ||
+                    (s5SctpPolicy->session_timeout < S5_MIN_SSN_TIMEOUT))
+                {
+                    FatalError("%s(%d) => Invalid timeout in config file.  "
+                        "Must be between %d and %d\n",
+                        file_name, file_line,
+                        S5_MIN_SSN_TIMEOUT, S5_MAX_SSN_TIMEOUT);
+                }
+
+                if (s_toks > 2)
+                {
+                    FatalError("%s(%d) => Invalid Stream5 SCTP Policy option.  Missing comma?\n",
+                        file_name, file_line);
+                }
+            }
+            else if (!strcasecmp(stoks[0], "ignore_any_rules"))
+            {
+                s5SctpPolicy->flags |= STREAM5_CONFIG_IGNORE_ANY;
+
+                if (s_toks > 1)
+                {
+                    FatalError("%s(%d) => Invalid Stream5 SCTP Policy option.  Missing comma?\n",
+                        file_name, file_line);
+                }
+            }
+            else
+            {
+                FatalError("%s(%d) => Invalid Stream5 SCTP Policy option\n",
+                            file_name, file_line);
+            }
+
+            mSplitFree(&stoks, s_toks);
+            i++;
+        }
+
+        mSplitFree(&toks, num_toks);
+    }
+
+    if (s5SctpPolicy->bound_addrs == NULL)
+    {
+        if (config->default_policy != NULL)
+        {
+            FatalError("%s(%d) => Default Stream5 SCTP Policy already set. "
+                       "This policy must be bound to a specific host or "
+                       "network.\n", file_name, file_line);
+        }
+
+        config->default_policy = s5SctpPolicy;
+    }
+    else
+    {
+        if (s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY)
+        {
+            FatalError("%s(%d) => \"ignore_any_rules\" option can be used only"
+                       " with Default Stream5 SCTP Policy\n", file_name, file_line);
+        }
+    }
+}
+
+static void Stream5PrintSctpConfig(Stream5SctpPolicy *s5SctpPolicy)
+{
+    LogMessage("Stream5 SCTP Policy config:\n");
+    LogMessage("    Timeout: %d seconds\n", s5SctpPolicy->session_timeout);
+    if (s5SctpPolicy->flags)
+    {
+        LogMessage("    Options:\n");
+        if (s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY)
+        {
+            LogMessage("        Ignore Any -> Any Rules: YES\n");
+        }
+    }
+    //IpAddrSetPrint("    Bound Addresses:", s5SctpPolicy->bound_addrs);
+}
+
+
+int Stream5VerifySctpConfig(Stream5SctpConfig *config, tSfPolicyId policyId)
+{
+    if (config == NULL)
+        return -1;
+
+    if (!sctp_lws_cache)
+        return -1;
+
+    if (config->num_policies == 0)
+        return -1;
+
+    /* Post-process SCTP rules to establish SCTP ports to inspect. */
+    setPortFilterList(config->port_filter, IPPROTO_SCTP,
+            (config->default_policy->flags & STREAM5_CONFIG_IGNORE_ANY), policyId);
+
+    //printf ("SCTP Ports with Inspection/Monitoring\n");
+    //s5PrintPortFilter(config->port_filter);
+    return 0;
+}
+
+#ifdef DEBUG_STREAM5
+static void PrintSctpSession(SctpSession *us)
+{
+    LogMessage("SctpSession:\n");
+    LogMessage("    ssn_time:           %lu\n", us->ssn_time.tv_sec);
+    LogMessage("    sender IP:          0x%08X\n", us->sctp_sender_ip);
+    LogMessage("    responder IP:          0x%08X\n", us->sctp_responder_ip);
+    LogMessage("    sender port:        %d\n", us->sctp_sender_port);
+    LogMessage("    responder port:        %d\n", us->sctp_responder_port);
+    LogMessage("    flags:              0x%X\n", us->lwSsn->session_flags);
+}
+#endif
+
+Stream5LWSession *GetLWSctpSession(SessionKey *key)
+{
+    return GetLWSessionFromKey(sctp_lws_cache, key);
+}
+
+void SctpSessionCleanup(Stream5LWSession *lwssn)
+{
+    SctpSession *sctpssn = NULL;
+
+    if (lwssn->session_flags & SSNFLAG_PRUNED)
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED);
+    }
+    else if (lwssn->session_flags & SSNFLAG_TIMEDOUT)
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT);
+    }
+    else
+    {
+        CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY);
+    }
+
+    if (lwssn->proto_specific_data)
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+    if (!sctpssn)
+    {
+        /* Huh? */
+        return;
+    }
+
+    /* Cleanup the proto specific data */
+    mempool_free(&sctp_session_mempool, lwssn->proto_specific_data);
+    lwssn->proto_specific_data = NULL;
+    lwssn->session_state = STREAM5_STATE_NONE;
+    lwssn->session_flags = SSNFLAG_NONE;
+    lwssn->expire_time = 0;
+    lwssn->ignore_direction = 0;
+
+    Stream5ResetFlowBits(lwssn);
+    FreeLWApplicationData(lwssn);
+
+    s5stats.sctp_sessions_released++;
+
+    RemoveSCTPSession(&sfBase);
+}
+
+uint32_t Stream5GetSctpPrunes(void)
+{
+    return sctp_lws_cache ? sctp_lws_cache->prunes : s5stats.sctp_prunes;
+}
+
+void Stream5ResetSctpPrunes(void)
+{
+    if ( sctp_lws_cache )
+        sctp_lws_cache->prunes = 0;
+}
+
+void Stream5ResetSctp(void)
+{
+    PurgeLWSessionCache(sctp_lws_cache);
+    mempool_clean(&sctp_session_mempool);
+}
+
+void Stream5CleanSctp(void)
+{
+    if ( sctp_lws_cache )
+        s5stats.sctp_prunes = sctp_lws_cache->prunes;
+
+    /* Clean up hash table -- delete all sessions */
+    DeleteLWSessionCache(sctp_lws_cache);
+    sctp_lws_cache = NULL;
+
+    mempool_destroy(&sctp_session_mempool);
+}
+
+static int NewSctpSession(Packet *p,
+                         Stream5LWSession *lwssn,
+                         Stream5SctpPolicy *s5SctpPolicy)
+{
+    SctpSession *tmp;
+    MemBucket *tmpBucket;
+    /******************************************************************
+     * create new sessions
+     *****************************************************************/
+    tmpBucket = mempool_alloc(&sctp_session_mempool);
+    if (tmpBucket == NULL)
+        return -1;
+
+    tmp = tmpBucket->data;
+    DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                "Creating new session tracker!\n"););
+
+    tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec;
+    tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec;
+    lwssn->session_flags |= SSNFLAG_SEEN_SENDER;
+
+    DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                "adding SctpSession to lightweight session\n"););
+    lwssn->proto_specific_data = tmpBucket;
+    lwssn->protocol = GET_IPH_PROTO(p);
+    lwssn->direction = FROM_SENDER;
+    tmp->lwSsn = lwssn;
+
+#ifdef DEBUG_STREAM5
+    PrintSctpSession(tmp);
+#endif
+    Stream5SetExpire(p, lwssn, s5SctpPolicy->session_timeout);
+
+    s5stats.sctp_sessions_created++;
+
+    AddSCTPSession(&sfBase);
+    if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP))
+        UpdateFlowIPState(&sfFlow, IP_ARG(lwssn->client_ip), IP_ARG(lwssn->server_ip), SFS_STATE_SCTP_CREATED);
+
+    return 0;
+}
+
+
+/*
+ * Main entry point for SCTP
+ */
+int Stream5ProcessSctp(Packet *p, Stream5LWSession *lwssn,
+                      Stream5SctpPolicy *s5SctpPolicy, SessionKey *skey)
+{
+    SFXHASH_NODE *hash_node = NULL;
+
+// XXX-IPv6 Stream5ProcessSCTP debugging
+
+    if (s5SctpPolicy == NULL)
+    {
+        int policyIndex;
+
+        /* Find an Sctp policy for this packet */
+        for (policyIndex = 0; policyIndex < s5_sctp_eval_config->num_policies; policyIndex++)
+        {
+            s5SctpPolicy = s5_sctp_eval_config->policy_list[policyIndex];
+
+            if (s5SctpPolicy->bound_addrs == NULL)
+                continue;
+
+            /*
+             * Does this policy handle packets to this IP address?
+             */
+            if(sfvar_ip_in(s5SctpPolicy->bound_addrs, GET_DST_IP(p)))
+            {
+                DEBUG_WRAP(DebugMessage(DEBUG_STREAM,
+                                        "[Stream5] Found sctp policy in IpAddrSet\n"););
+                break;
+            }
+        }
+
+        if (policyIndex == s5_sctp_eval_config->num_policies)
+            s5SctpPolicy = s5_sctp_eval_config->default_policy;
+
+        if (!s5SctpPolicy)
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM,
+                                    "[Stream5] Could not find Sctp Policy context "
+                                    "for IP %s\n", inet_ntoa(GET_DST_ADDR(p))););
+            return 0;
+        }
+    }
+
+      /* SCTP Sessions required */
+    if (lwssn == NULL)
+    {
+        if ((isPacketFilterDiscard(p, s5SctpPolicy->flags & STREAM5_CONFIG_IGNORE_ANY) == PORT_MONITOR_PACKET_DISCARD)
+                && !StreamExpectIsExpected(p, &hash_node))
+        {
+            //ignore the packet
+            UpdateFilteredPacketStats(&sfBase, IPPROTO_SCTP);
+            return 0;
+        }
+        /* Create a new session, mark SENDER seen */
+        lwssn = NewLWSession(sctp_lws_cache, p, skey, (void *)s5SctpPolicy);
+        s5stats.total_sctp_sessions++;
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+            "Stream5: Retrieved existing session object.\n"););
+    }
+
+    if (!lwssn)
+    {
+        LogMessage("Stream5: Failed to retrieve session object.  Out of memory?\n");
+        return -1;
+    }
+
+    p->ssnptr = lwssn;
+
+    /*
+     * Check if the session is expired.
+     * Should be done before we do something with the packet...
+     * ie, Insert a packet, or handle state change SYN, FIN, RST, etc.
+     */
+    if ((lwssn->session_state & STREAM5_STATE_TIMEDOUT) ||
+        Stream5Expire(p, lwssn))
+    {
+        lwssn->session_flags |= SSNFLAG_TIMEDOUT;
+
+        /* Session is timed out */
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5 SCTP session timedout!\n"););
+
+        /* Clean it up */
+        SctpSessionCleanup(lwssn);
+
+        ProcessSctp(lwssn, p, s5SctpPolicy, hash_node);
+    }
+    else
+    {
+        ProcessSctp(lwssn, p, s5SctpPolicy, hash_node);
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Finished Stream5 SCTP cleanly!\n"
+                    "---------------------------------------------------\n"););
+    }
+    MarkupPacketFlags(p, lwssn);
+    Stream5SetExpire(p, lwssn, s5SctpPolicy->session_timeout);
+
+    return 0;
+}
+
+static int ProcessSctp(Stream5LWSession *lwssn, Packet *p,
+        Stream5SctpPolicy *s5SctpPolicy, SFXHASH_NODE *hash_node)
+{
+    char ignore;
+    SctpSession *sctpssn = NULL;
+
+    DEBUG_WRAP(
+            char *t = NULL;
+            char *l = NULL;
+            );
+
+    if (lwssn->proto_specific_data != NULL)
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+    if (lwssn->protocol != IPPROTO_SCTP)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Lightweight session not SCTP on SCTP packet\n"););
+        return ACTION_NOTHING;
+    }
+
+    if (lwssn->session_flags & (SSNFLAG_DROP_CLIENT|SSNFLAG_DROP_SERVER))
+    {
+        /* Got a packet on a session that was dropped (by a rule). */
+        GetLWPacketDirection(p, lwssn);
+
+        /* Drop this packet */
+        if (((p->packet_flags & PKT_FROM_SERVER) &&
+             (lwssn->session_flags & SSNFLAG_DROP_SERVER)) ||
+            ((p->packet_flags & PKT_FROM_CLIENT) &&
+             (lwssn->session_flags & SSNFLAG_DROP_CLIENT)))
+        {
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                        "Blocking %s packet as session was blocked\n",
+                        p->packet_flags & PKT_FROM_SERVER ?
+                        "server" : "client"););
+            DisableDetect(p);
+            /* Still want to add this number of bytes to totals */
+            SetPreprocBit(p, PP_PERFMONITOR);
+            Active_DropPacket();
+#ifdef ACTIVE_RESPONSE
+            Stream5ActiveResponse(p, lwssn);
+#endif
+            return ACTION_NOTHING;
+        }
+    }
+
+    if (sctpssn == NULL)
+    {
+        lwssn->direction = FROM_SENDER;
+        IP_COPY_VALUE(lwssn->client_ip, GET_SRC_IP(p));
+        lwssn->client_port = p->sctph->sh_sport;
+        IP_COPY_VALUE(lwssn->server_ip, GET_DST_IP(p));
+        lwssn->server_port = p->sctph->sh_dport;
+        lwssn->session_state |= STREAM5_STATE_SENDER_SEEN;
+        if (NewSctpSession(p, lwssn, s5SctpPolicy) == -1)
+            return ACTION_NOTHING;
+        sctpssn = (SctpSession *)lwssn->proto_specific_data->data;
+
+        /* Check if the session is to be ignored */
+        if (hash_node)
+            ignore = SteamExpectProcessNode(p, lwssn, hash_node);
+        else
+            ignore = SteamExpectCheck(p, lwssn);
+        if (ignore)
+        {
+            /* Set the directions to ignore... */
+            lwssn->ignore_direction = ignore;
+            DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                        "Stream5: Ignoring packet from %d. "
+                        "Marking session marked as ignore.\n",
+                        p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
+            Stream5DisableInspection(lwssn, p);
+            return ACTION_NOTHING;
+        }
+    }
+
+    /* figure out direction of this packet */
+    GetLWPacketDirection(p, lwssn);
+
+    if (((p->packet_flags & PKT_FROM_SERVER) && (lwssn->ignore_direction & SSN_DIR_CLIENT)) ||
+        ((p->packet_flags & PKT_FROM_CLIENT) && (lwssn->ignore_direction & SSN_DIR_SERVER)))
+    {
+        Stream5DisableInspection(lwssn, p);
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5 Ignoring packet from %d. "
+                    "Session marked as ignore\n",
+                    p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder"););
+        return ACTION_NOTHING;
+    }
+
+    /* if both seen, mark established */
+    if(p->packet_flags & PKT_FROM_SERVER)
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5: Updating on packet from responder\n"););
+        lwssn->session_flags |= SSNFLAG_SEEN_RESPONDER;
+#ifdef ACTIVE_RESPONSE
+        SetTTL(lwssn, p, 0);
+#endif
+
+        DEBUG_WRAP(
+                t = "Responder";
+                l = "Sender");
+    }
+    else
+    {
+        DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
+                    "Stream5: Updating on packet from client\n"););
+        /* if we got here we had to see the SYN already... */
+        lwssn->session_flags |= SSNFLAG_SEEN_SENDER;
+#ifdef ACTIVE_RESPONSE
+        SetTTL(lwssn, p, 1);
+#endif
+
+        DEBUG_WRAP(
+                t = "Sender";
+                l = "Responder");
+    }
+
+    if (!(lwssn->session_flags & SSNFLAG_ESTABLISHED))
+    {
+        if ((lwssn->session_flags & SSNFLAG_SEEN_SENDER) &&
+            (lwssn->session_flags & SSNFLAG_SEEN_RESPONDER))
+        {
+            lwssn->session_flags |= SSNFLAG_ESTABLISHED;
+        }
+    }
+
+    return ACTION_NOTHING;
+}
+
+void SctpUpdateDirection(Stream5LWSession *ssn, char dir,
+                        snort_ip_p ip, uint16_t port)
+{
+    SctpSession *sctpssn = (SctpSession *)ssn->proto_specific_data->data;
+    snort_ip tmpIp;
+    uint16_t tmpPort;
+
+    if (IP_EQUALITY(&sctpssn->sctp_sender_ip, ip) && (sctpssn->sctp_sender_port == port))
+    {
+        if ((dir == SSN_DIR_SENDER) && (ssn->direction == SSN_DIR_SENDER))
+        {
+            /* Direction already set as SENDER */
+            return;
+        }
+    }
+    else if (IP_EQUALITY(&sctpssn->sctp_responder_ip, ip) && (sctpssn->sctp_responder_port == port))
+    {
+        if ((dir == SSN_DIR_RESPONDER) && (ssn->direction == SSN_DIR_RESPONDER))
+        {
+            /* Direction already set as RESPONDER */
+            return;
+        }
+    }
+
+    /* Swap them -- leave ssn->direction the same */
+    tmpIp = sctpssn->sctp_sender_ip;
+    tmpPort = sctpssn->sctp_sender_port;
+    sctpssn->sctp_sender_ip = sctpssn->sctp_responder_ip;
+    sctpssn->sctp_sender_port = sctpssn->sctp_responder_port;
+    sctpssn->sctp_responder_ip = tmpIp;
+    sctpssn->sctp_responder_port = tmpPort;
+}
+
+void s5SctpSetPortFilterStatus(unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    if (parsing && (s5_swap_config != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return;
+
+    sctp_config->port_filter[port] |= status;
+}
+
+void s5SctpUnsetPortFilterStatus(unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    if (parsing && (s5_swap_config != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return;
+
+    sctp_config->port_filter[port] &= ~status;
+}
+
+int s5SctpGetPortFilterStatus(unsigned short port, tSfPolicyId policyId, int parsing)
+{
+    Stream5Config *config;
+    Stream5SctpConfig *sctp_config;
+
+#ifdef SNORT_RELOAD
+    if (parsing && (s5_swap_config != NULL))
+        config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
+    else
+#endif
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_config, policyId);
+
+    if (config == NULL)
+        return PORT_MONITOR_NONE;
+
+    sctp_config = config->sctp_config;
+    if (sctp_config == NULL)
+        return PORT_MONITOR_NONE;
+
+    return (int)sctp_config->port_filter[port];
+}
+
+void Stream5SctpConfigFree(Stream5SctpConfig *config)
+{
+    int i;
+
+    if (config == NULL)
+        return;
+
+    /* Cleanup SCTP Policies and the list */
+    for (i = 0; i < config->num_policies; i++)
+    {
+        Stream5SctpPolicy *policy = config->policy_list[i];
+
+        if (policy->bound_addrs != NULL)
+            sfvar_free(policy->bound_addrs);
+        free(policy);
+    }
+
+    free(config->policy_list);
+    free(config);
+}
+
diff --git a/src/preprocessors/Stream5/snort_stream5_sctp.h b/src/preprocessors/Stream5/snort_stream5_sctp.h
new file mode 100644
index 0000000..e955195
--- /dev/null
+++ b/src/preprocessors/Stream5/snort_stream5_sctp.h
@@ -0,0 +1,264 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2005-2013 Sourcefire, Inc.
+ *
+ * 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 SNORT_STREAM5_SCTP_H_
+#define SNORT_STREAM5_SCTP_H_
+
+#include "ipv6_port.h"
+#include "stream5_common.h"
+#include "stream5_paf.h"
+#include "sfPolicy.h"
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpDataBlock
+ * @sip: Snort IP structure for source.
+ * @dip: Snort IP structure for destination.
+ * @vtag: Verification tag from the SCTP common header.
+ * @tsn: Transmission Sequence Number from the DATA chunk.
+ * @length: Chunk length.
+ * @sid: Stream ID.
+ * @ssn: Stream Sequence Number.
+ * @flags: DATA chunk flags field (IUBE).
+ * @pad: Padding to align on a 32-bit boundary.
+ */
+/*
+typedef struct _SctpDataBlock
+{
+    snort_ip     sip;
+    snort_ip     dip;
+    uint32_t     vtag;
+    uint32_t     tsn;
+    uint16_t     length;
+    uint16_t     sid;
+    uint16_t     ssn;
+    uint8_t      flags;
+    uint8_t      pad;
+} SctpDataBlock;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpStreamSegment
+ * @pkth: Pointer to packet header from DAQ.
+ * @pktOrig: Pointer to original Packet structure.
+ * @pkt: Pointer to Packet structure (modified?).
+ * @data: Pointer to data (?).
+ * @payload: Pointer to packet payload.
+ * @prev: Pointer to previous SctpStreamMessage.
+ * @next: Pointer to next SctpStreamMessage.
+ * @vtag: Verification tag.
+ * @tsn: Transmission Sequence Number.
+ * @length: Chunk length.
+ * @sid: Stream ID.
+ * @ssn: Stream Sequence Number.
+ * @cflags: Chunk flags.
+ * @msgflags: SctpStreamMessage flags.
+ */
+/*
+typedef struct _SctpStreamMessage
+{
+    DAQ_PktHdr_t pkth;
+    uint8_t      *pktOrig;
+    uint8_t      *pkt;
+    uint8_t      *data;
+    uint8_t      *payload;
+    struct       _SctpStreamMessage *prev;
+    struct       _SctpStreamMessage *next;
+    uint32_t     vtag;
+    uint32_t     tsn;
+    uint32_t     caplen;
+    uint16_t     length;
+    uint16_t     sid;
+    uint16_t     ssn;
+    uint8_t      cflags;
+    uint8_t      msgflags;
+} SctpStreamMessage;
+*/
+
+
+#define SCTP_S5_HMAC_SHA1           0x0001        /* HMAC SHA-1 */
+#define SCTP_S5_HMAC_SHA256         0x0002        /* HMAC SHA-256 */
+
+#define SCTP_S5_EXT_ASCONF          0x0001        /* ASCONF chunk supported. */
+#define SCTP_S5_EXT_ASCONF_ACK      0x0002        /* ASCONF-ACK chunk supported. */
+#define SCTP_S5_EXT_AUTH            0x0004        /* AUTH chunk supported. */
+#define SCTP_S5_EXT_FWD_TSN         0x0008        /* FORWARD-TSN chunk supported. */
+#define SCTP_S5_EXT_PKTDROP         0x0010        /* PKTDROP chunk supported. */
+#define SCTP_S5_EXT_RECONFIG        0x0020        /* RE-CONFIG chunk supported. */
+
+#define SCTP_S5_FLAG_ECN            0x0001        /* ECN parameter (0x8000). */
+#define SCTP_S5_FLAG_NONCE          0x0002        /* Nonce-Supported parameter (0x8001). */
+#define SCTP_S5_FLAG_FWD_TSN        0x0004        /* FORWARD-TSN parameter (0xc000). */
+
+#define SCTP_S5_ADDR_IPV4           0x0001        /* IPv4 addresses supported. */
+#define SCTP_S5_ADDR_IPV6           0x0002        /* IPv6 addresses supported. */
+#define SCTP_S5_ADDR_DNS            0x0004        /* Hostnames supported. */
+
+#define SCTP_S5_AUTH_DATA           0x00000001    /* Authenticated DATA chunk */
+#define SCTP_S5_AUTH_SACK           0x00000002    /* Authenticated SACK chunk */
+#define SCTP_S5_AUTH_HEARTBEAT      0x00000004    /* Authenticated HEARTBEAT chunk */
+#define SCTP_S5_AUTH_HEARTBEAT_ACK  0x00000008    /* Authenticated HEARTBEAT-ACK chunk */
+#define SCTP_S5_AUTH_ABORT          0x00000010    /* Authenticated ABORT chunk */
+#define SCTP_S5_AUTH_SHUTDOWN       0x00000020    /* Authenticated SHUTDOWN chunk */
+#define SCTP_S5_AUTH_SHUTDOWN_ACK   0x00000040    /* Authenticated SHUTDOWN-ACK chunk */
+#define SCTP_S5_AUTH_ERROR          0x00000080    /* Authenticated ERROR chunk */
+#define SCTP_S5_AUTH_ECNE           0x00000100    /* Authenticated ECNE chunk */
+#define SCTP_S5_AUTH_CWR            0x00000200    /* Authenticated CWR chunk */
+#define SCTP_S5_AUTH_ASCONF_ACK     0x00000400    /* Authenticated ASCONF-ACK chunk */
+#define SCTP_S5_AUTH_PKTDROP        0x00000800    /* Authenticated PKTDROP chunk */
+#define SCTP_S5_AUTH_RECONFIG       0x00001000    /* Authenticated RE-CONFIG chunk */
+#define SCTP_S5_AUTH_PAD            0x00002000    /* Authenticated PAD chunk */
+#define SCTP_S5_AUTH_FORWARD_TSN    0x00004000    /* Authenticated FORWARD-TSN chunk */
+#define SCTP_S5_AUTH_ASCONF         0x00008000    /* Authenticated ASCONF chunk */
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpPeerProperties
+ * @extensions: uint16_t flag variable of supported SCTP extensions.
+ * @flags: uint16_t flag variable of miscellaneous SCTP bits (not extensions).
+ * @addrtypes: uint16_t flag variable of the supported address types.
+ * @cookie_len: uint16_t value to hold the size of the state cookie.
+ * @cookie: pointer to a MemBucket to hold the state cookie.
+ */
+/*
+typedef struct _SctpPeerBits
+{
+    uint32_t auth_chunks;
+    uint16_t extensions;
+    uint16_t flags;
+    uint16_t addr_types;
+    uint16_t hmac_types;
+    uint16_t cookie_len;
+    uint8_t pad;
+    uint8_t *cookie;
+} SctpPeerBits;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpPeers
+ * @client_bits: pointer to client-specific SctpPeerBits structure.
+ * @server_bits: pointer to server-specific SctpPeerBits structure.
+ */
+/*
+typedef struct _SctpPeers
+{
+    SctpPeerBits *client_bits;
+    SctpPeerBits *server_bits;
+} SctpPeers;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpStreamTracker
+ * @flush_mgr: Pointer to FlushMgr structure.
+ * @paf_state: Pointer to PAF_State structure (DO NOT MODIFY!)
+ * @alerts[]: Array of Stream5AlertInfo structures.
+ * @sctp_policy: Pointer to Stream5SctpPolicy structure.
+ * @msghead: Pointer to first queued message.
+ * @msgtail: Pointer to last queued message.
+ * @msgnext: Pointer to next queued message (to flush?).
+ * @vtag: Verification tag.
+ * @itag: Initiate tag from the INIT or INIT-ACK.
+ * @init_tsn: Initial TSN for this side of the conversation.
+ * @l_unsack_tsn: [Local]: UnSACK'ed TSN.
+ * @l_next_tsn: [Local]: Next expected TSN.
+ * @r_next_tsn: [Remote]: Next expected TSN.
+ * @msghead_tsn: TSN of first queued message.
+ * @msg_count: Number of currently queued messages.
+ * @msg_bytes_total: Total amount of message bytes queued.
+ * @msg_flush_count: Number of flushed queued messages.
+ * @macaddr: MAC Address.
+ * @flags: Bitmap flags (TF_*).
+ * @state: SCTP Stream State.
+ * @alert_count: Number of alerts stored (up to MAX_SESSION_ALERTS). 
+ * @pad: Padding to align on a 32-bit boundary.
+ */
+/*
+typedef struct _SctpStreamTracker
+{
+    FlushMgr  flush_mgr;
+    PAF_State paf_state;
+    Stream5AlertInfo alerts[MAX_SESSION_ALERTS];
+    Stream5SctpPolicy *sctp_policy;
+    SctpStreamMessage *msghead;
+    SctpStreamMessage *msgtail;
+    SctpStreamMessage *msgnext;
+    SctpPeerBits *peerbits;
+    uint64_t macaddr;
+    uint32_t vtag;
+    uint32_t itag;
+    uint32_t a_rwnd;
+    uint32_t init_tsn;
+    uint32_t l_unsack_tsn;
+    uint32_t l_next_tsn;
+    uint32_t r_next_tsn;
+    uint32_t msghead_tsn;
+    uint32_t msg_count;
+    uint32_t msg_bytes_total;
+    uint32_t msg_flush_count;
+    uint16_t nr_out_streams;
+    uint16_t nr_in_streams;
+    uint8_t  flags;
+    uint8_t  state;
+    uint8_t  alert_count;
+    uint8_t  pad;
+} SctpStreamTracker;
+*/
+
+/* XXX: Not implemented yet! */
+/**
+ * struct SctpAssociation
+ * @client: Pointer to SctpStreamTracker structure for the client.
+ * @server: Pointer to SctpStreamTracker structure for the server.
+ * @lwssn: Lightweight session data.
+ * @ssn_time: [DEBUG]: Session time.
+ */
+/*
+typedef struct _SctpAssociation
+{
+    SctpStreamTracker *client;
+    SctpStreamTracker *server;
+    Stream5LWSession *lwssn;
+#ifdef DEBUG
+    struct timeval ssn_time;
+#endif
+} SctpAssociation;
+*/
+
+
+/* Prototypes. */
+void Stream5CleanSctp(void);
+void Stream5ResetSctp(void);
+void Stream5InitSctp(Stream5GlobalConfig *);
+void Stream5SctpPolicyInit(Stream5SctpConfig *, char *);
+int Stream5VerifySctpConfig(Stream5SctpConfig *, tSfPolicyId);
+int Stream5ProcessSctp(Packet *, Stream5LWSession *, Stream5SctpPolicy *, SessionKey *);
+void SctpUpdateDirection(Stream5LWSession *ssn, char dir, snort_ip_p ip, uint16_t port);
+Stream5LWSession *GetLWSctpSession(SessionKey *key);
+void s5SctpSetPortFilterStatus(unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing);
+int s5SctpGetPortFilterStatus(unsigned short port, tSfPolicyId policyId, int parsing);
+void Stream5SctpConfigFree(Stream5SctpConfig *);
+uint32_t Stream5GetSctpPrunes(void);
+void Stream5ResetSctpPrunes(void);
+
+#endif /* SNORT_STREAM5_SCTP_H_ */
diff --git a/src/preprocessors/Stream5/snort_stream5_session.c b/src/preprocessors/Stream5/snort_stream5_session.c
index 3cdb971..5c5da29 100644
--- a/src/preprocessors/Stream5/snort_stream5_session.c
+++ b/src/preprocessors/Stream5/snort_stream5_session.c
@@ -4,6 +4,9 @@
 ** Copyright (C) 2005-2013 Sourcefire, Inc.
 ** AUTHOR: Steven Sturges <ssturges () sourcefire com>
 **
+** SCTP bits Copyright (C) 2013
+** Joshua Kinard <kumba12345 () gmail com>
+**
 ** 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
@@ -116,6 +119,7 @@ int GetLWSessionKeyFromIpPort(
         {
             case IPPROTO_TCP:
             case IPPROTO_UDP:
+            case IPPROTO_SCTP:
                 sport = srcPort;
                 dport = dstPort;
                 break;
@@ -182,6 +186,7 @@ int GetLWSessionKeyFromIpPort(
         {
             case IPPROTO_TCP:
             case IPPROTO_UDP:
+            case IPPROTO_SCTP:
                 sport = srcPort;
                 dport = dstPort;
                 break;
@@ -308,6 +313,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_SERVER;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_sport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_CLIENT;
@@ -337,6 +353,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_CLIENT;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_dport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_SERVER;
@@ -369,6 +396,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_SERVER;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_sport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_CLIENT;
@@ -398,6 +436,17 @@ void GetLWPacketDirection(Packet *p, Stream5LWSession *ssn)
                     p->packet_flags |= PKT_FROM_CLIENT;
                 }
             }
+            else if (GET_IPH_PROTO(p) == IPPROTO_SCTP)
+            {
+                if (p->sctph->sh_dport == ssn->client_port)
+                {
+                    p->packet_flags |= PKT_FROM_SERVER;
+                }
+                else
+                {
+                    p->packet_flags |= PKT_FROM_CLIENT;
+                }
+            }
             else
             {
                 p->packet_flags |= PKT_FROM_SERVER;
@@ -508,6 +557,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
     Stream5GlobalConfig *save_global_eval_config = s5_global_eval_config;
     Stream5TcpConfig *save_tcp_eval_config = s5_tcp_eval_config;
     Stream5UdpConfig *save_udp_eval_config = s5_udp_eval_config;
+    Stream5SctpConfig *save_sctp_eval_config = s5_sctp_eval_config;
     Stream5IcmpConfig *save_icmp_eval_config = s5_icmp_eval_config;
 
     int ret;
@@ -531,7 +581,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
     sfip_set_ip(&server_ip, &ssn->server_ip);
 
     /*
-     * Call callback to cleanup the protocol (TCP/UDP/ICMP)
+     * Call callback to cleanup the protocol (TCP/UDP/SCTP/ICMP)
      * specific session details
      */
     if (sessionCache->cleanup_fcn)
@@ -575,6 +625,7 @@ int DeleteLWSession(Stream5SessionCache *sessionCache,
     s5_global_eval_config = save_global_eval_config;
     s5_tcp_eval_config = save_tcp_eval_config;
     s5_udp_eval_config = save_udp_eval_config;
+    s5_sctp_eval_config = save_sctp_eval_config;
     s5_icmp_eval_config = save_icmp_eval_config;
 
     return ret;
@@ -1113,6 +1164,11 @@ Stream5SetRuntimeConfiguration(
                 return -1;
             s5_udp_eval_config = pPolicyConfig->udp_config;
             break;
+        case IPPROTO_SCTP:
+            if (pPolicyConfig->sctp_config == NULL)
+                return -1;
+            s5_sctp_eval_config = pPolicyConfig->sctp_config;
+            break;
         case IPPROTO_ICMP:
             if (pPolicyConfig->icmp_config == NULL)
                 return -1;
diff --git a/src/preprocessors/Stream5/stream5_common.c b/src/preprocessors/Stream5/stream5_common.c
index 829aad9..fd8fda3 100644
--- a/src/preprocessors/Stream5/stream5_common.c
+++ b/src/preprocessors/Stream5/stream5_common.c
@@ -39,6 +39,7 @@
 #include "sp_dynamic.h"
 #include "snort_stream5_tcp.h"
 #include "snort_stream5_udp.h"
+#include "snort_stream5_sctp.h"
 #include "snort_stream5_icmp.h"
 #include "snort_stream5_ip.h"
 #include "parser.h"
@@ -92,6 +93,10 @@ int Stream5Expire(Packet *p, Stream5LWSession *lwssn)
                 s5stats.udp_timeouts++;
                 //DeleteLWSession(udp_lws_cache, lwssn);
                 break;
+            case IPPROTO_SCTP:
+                s5stats.sctp_timeouts++;
+                //DeleteLWSession(sctp_lws_cache, lwssn);
+                break;
             case IPPROTO_ICMP:
                 s5stats.icmp_timeouts++;
                 //DeleteLWSession(icmp_lws_cache, lwssn);
@@ -206,6 +211,8 @@ static inline RuleTreeNode * protocolRuleList(RuleListNode *rule, int protocol)
             return rule->RuleList->TcpList;
         case IPPROTO_UDP:
             return rule->RuleList->UdpList;
+        case IPPROTO_SCTP:
+            return rule->RuleList->SctpList;
         case IPPROTO_ICMP:
             break;
         default:
@@ -216,15 +223,17 @@ static inline RuleTreeNode * protocolRuleList(RuleListNode *rule, int protocol)
 #endif
 static inline char * getProtocolName (int protocol)
 {
-    static char *protocolName[] = {"TCP", "UDP", "ICMP"};
+    static char *protocolName[] = {"TCP", "UDP", "SCTP", "ICMP"};
     switch (protocol)
     {
         case IPPROTO_TCP:
             return protocolName[0];
         case IPPROTO_UDP:
             return protocolName[1];
-        case IPPROTO_ICMP:
+        case IPPROTO_SCTP:
             return protocolName[2];
+        case IPPROTO_ICMP:
+            return protocolName[3];
             break;
         default:
             break;
@@ -270,7 +279,7 @@ void setPortFilterList(
     OptTreeNode *otn;
     int inspectSrc, inspectDst;
     char any_any_flow = 0;
-    IgnoredRuleList *pIgnoredRuleList = NULL;     ///list of ignored rules
+    IgnoredRuleList *pIgnoredRuleList = NULL;     /* List of ignored rules */
     char *protocolName;
     SFGHASH_NODE *hashNode;
     int flowBitIsSet = 0;
@@ -282,7 +291,9 @@ void setPortFilterList(
                    __FILE__, __LINE__);
     }
 
-    if ((protocol == IPPROTO_TCP) && (ignoreAnyAnyRules == 0))
+    if (((protocol == IPPROTO_TCP) ||
+         (protocol == IPPROTO_SCTP)) &&
+        (ignoreAnyAnyRules == 0))
     {
         int j;
         for (j=0; j<MAX_PORTS; j++)
@@ -294,14 +305,13 @@ void setPortFilterList(
 
     protocolName = getProtocolName(protocol);
 
-    /* Post-process TCP rules to establish TCP ports to inspect. */
+    /* Post-process TCP/SCTP rules to establish TCP/SCTP ports to inspect. */
     for (hashNode = sfghash_findfirst(sc->otn_map);
          hashNode;
          hashNode = sfghash_findnext(sc->otn_map))
     {
         otn = (OptTreeNode *)hashNode->data;
         flowBitIsSet = Stream5OtnHasFlowOrFlowbit(otn);
-
         rtn = getRtnFromOtn(otn, policyId);
 
         if (!rtn)
@@ -311,29 +321,28 @@ void setPortFilterList(
 
         if (rtn->proto == protocol)
         {
-            //do operation
+            /* Do operation */
             inspectSrc = inspectDst = 0;
             if (PortObjectHasAny(rtn->src_portobject))
-            {
                 inspectSrc = -1;
-            }
             else
             {
                 port_array = PortObjectCharPortArray(port_array, rtn->src_portobject, &num_ports);
                 if (port_array && num_ports != 0)
                 {
                     inspectSrc = 1;
-                    for (i=0;i<SFPO_MAX_PORTS;i++)
+                    for (i = 0; i < SFPO_MAX_PORTS; i++)
                     {
                         if (port_array[i])
                         {
                             portList[i] |= PORT_MONITOR_INSPECT;
+
                             /* port specific rule */
-                                /* Look for an OTN with flow or flowbits keyword */
-                                if (flowBitIsSet)
-                                {
-                                    portList[i] |= PORT_MONITOR_SESSION;
-                                }
+                            /* Look for an OTN with flow or flowbits keyword */
+                            if (flowBitIsSet)
+                            {
+                                portList[i] |= PORT_MONITOR_SESSION;
+                            }
                         }
                     }
                 }
@@ -343,6 +352,7 @@ void setPortFilterList(
                     port_array = NULL;
                 }
             }
+
             if (PortObjectHasAny(rtn->dst_portobject))
             {
                 inspectDst = -1;
@@ -353,16 +363,17 @@ void setPortFilterList(
                 if (port_array && num_ports != 0)
                 {
                     inspectDst = 1;
-                    for (i=0;i<SFPO_MAX_PORTS;i++)
+                    for (i = 0; i < SFPO_MAX_PORTS; i++)
                     {
                         if (port_array[i])
                         {
                             portList[i] |= PORT_MONITOR_INSPECT;
+
                             /* port specific rule */
-                                if (flowBitIsSet)
-                                {
-                                    portList[i] |= PORT_MONITOR_SESSION;
-                                }
+                            if (flowBitIsSet)
+                            {
+                                portList[i] |= PORT_MONITOR_SESSION;
+                            }
                         }
                     }
                 }
@@ -384,13 +395,14 @@ void setPortFilterList(
         }
     }
 
-    /* If portscan is tracking TCP/UDP, need to create
+    /* If portscan is tracking TCP/UDP/SCTP, need to create
      * sessions for all ports */
     if (((protocol == IPPROTO_UDP) && (ps_get_protocols(policyId) & PS_PROTO_UDP))
-     || ((protocol == IPPROTO_TCP) && (ps_get_protocols(policyId) & PS_PROTO_TCP)))
+     || ((protocol == IPPROTO_TCP) && (ps_get_protocols(policyId) & PS_PROTO_TCP))
+     || ((protocol == IPPROTO_SCTP) && (ps_get_protocols(policyId) & PS_PROTO_SCTP)))
     {
         int j;
-        for (j=0; j<MAX_PORTS; j++)
+        for (j = 0; j < MAX_PORTS; j++)
         {
             portList[j] |= PORT_MONITOR_SESSION;
         }
@@ -595,6 +607,12 @@ void Stream5FreeConfig(Stream5Config *config)
         config->udp_config = NULL;
     }
 
+    if (config->sctp_config != NULL)
+    {
+        Stream5SctpConfigFree(config->sctp_config);
+        config->sctp_config = NULL;
+    }
+
     if (config->icmp_config != NULL)
     {
         Stream5IcmpConfigFree(config->icmp_config);
diff --git a/src/preprocessors/Stream5/stream5_common.h b/src/preprocessors/Stream5/stream5_common.h
index 9bd7e0d..6cd2ae6 100644
--- a/src/preprocessors/Stream5/stream5_common.h
+++ b/src/preprocessors/Stream5/stream5_common.h
@@ -130,6 +130,21 @@
 
 #define TCP_HZ          100
 
+
+/* SCTP States. */
+/* XXX: Not implemented yet */
+#define STREAM5_SCTP_STATE_NONE                 0x0000
+#define STREAM5_SCTP_STATE_COOKIE_WAIT          0x0001
+#define STREAM5_SCTP_STATE_COOKIE_SENT          0x0002   /* Not in RFC4690 */
+#define STREAM5_SCTP_STATE_COOKIE_ECHOED        0x0004
+#define STREAM5_SCTP_STATE_ESTABLISHED          0x0008
+#define STREAM5_SCTP_STATE_SHUTDOWN_PENDING     0x0010
+#define STREAM5_SCTP_STATE_SHUTDOWN_SENT        0x0020
+#define STREAM5_SCTP_STATE_SHUTDOWN_RECEIVED    0x0040
+#define STREAM5_SCTP_STATE_SHUTDOWN_ACK_SENT    0x0080
+#define STREAM5_SCTP_STATE_CLOSED               0x0100
+
+
 /*  D A T A   S T R U C T U R E S  **********************************/
 typedef StreamSessionKey SessionKey;
 
@@ -194,10 +209,12 @@ typedef struct _Stream5GlobalConfig
     char       disabled;
     char       track_tcp_sessions;
     char       track_udp_sessions;
+    char       track_sctp_sessions;
     char       track_icmp_sessions;
     char       track_ip_sessions;
     uint32_t   max_tcp_sessions;
     uint32_t   max_udp_sessions;
+    uint32_t   max_sctp_sessions;
     uint32_t   max_icmp_sessions;
     uint32_t   max_ip_sessions;
     uint32_t   memcap;
@@ -313,6 +330,30 @@ typedef struct _Stream5UdpConfig
 
 } Stream5UdpConfig;
 
+typedef struct _Stream5SctpPolicy
+{
+    uint32_t   session_timeout;
+    uint32_t   max_queued_bytes;
+    uint32_t   max_queued_chunks;
+    uint32_t   hs_timeout;
+    uint16_t   flags;
+    IpAddrSet   *bound_addrs;
+    FlushConfig flush_config[MAX_PORTS];
+#ifdef TARGET_BASED
+    FlushConfig flush_config_protocol[MAX_PROTOCOL_ORDINAL];
+#endif
+} Stream5SctpPolicy;
+
+typedef struct _Stream5SctpConfig
+{
+    Stream5SctpPolicy *default_policy;
+    Stream5SctpPolicy **policy_list;
+    uint8_t num_policies;
+    uint8_t dummy;  /* For alignment */
+    uint16_t port_filter[MAX_PORTS + 1];
+
+} Stream5SctpConfig;
+
 typedef struct _Stream5IcmpPolicy
 {
     uint32_t   session_timeout;
@@ -344,6 +385,7 @@ typedef struct _Stream5Config
     Stream5GlobalConfig *global_config;
     Stream5TcpConfig *tcp_config;
     Stream5UdpConfig *udp_config;
+    Stream5SctpConfig *sctp_config;
     Stream5IcmpConfig *icmp_config;
     Stream5IpConfig *ip_config;
 
@@ -355,7 +397,7 @@ typedef struct _Stream5Config
 
 } Stream5Config;
 
-/**Common statistics for tcp and udp packets, maintained by port filtering.
+/* Common statistics for tcp, udp, and sctp packets, maintained by port filtering.
  */
 typedef struct {
     /**packets dropped without further processing by any preprocessor or
@@ -376,10 +418,12 @@ typedef struct _Stream5Stats
 {
     uint32_t   total_tcp_sessions;
     uint32_t   total_udp_sessions;
+    uint32_t   total_sctp_sessions;
     uint32_t   total_icmp_sessions;
     uint32_t   total_ip_sessions;
     uint32_t   tcp_prunes;
     uint32_t   udp_prunes;
+    uint32_t   sctp_prunes;
     uint32_t   icmp_prunes;
     uint32_t   ip_prunes;
     uint32_t   tcp_timeouts;
@@ -396,6 +440,12 @@ typedef struct _Stream5Stats
     uint32_t   udp_sessions_created;
     uint32_t   udp_sessions_released;
     uint32_t   udp_discards;
+    uint32_t   sctp_timeouts;
+    uint32_t   sctp_sessions_created;
+    uint32_t   sctp_sessions_released;
+    uint32_t   sctp_streammsgs_created;
+    uint32_t   sctp_streammsgs_released;
+    uint32_t   sctp_discards;
     uint32_t   icmp_timeouts;
     uint32_t   icmp_sessions_created;
     uint32_t   icmp_sessions_released;
@@ -404,6 +454,7 @@ typedef struct _Stream5Stats
     uint32_t   internalEvents;
     tPortFilterStats  tcp_port_filter;
     tPortFilterStats  udp_port_filter;
+    tPortFilterStats  sctp_port_filter;
 } Stream5Stats;
 
 /**Whether incoming packets should be ignored or processed.
@@ -468,6 +519,7 @@ extern uint32_t mem_in_use;
 extern Stream5GlobalConfig *s5_global_eval_config;
 extern Stream5TcpConfig *s5_tcp_eval_config;
 extern Stream5UdpConfig *s5_udp_eval_config;
+extern Stream5SctpConfig *s5_sctp_eval_config;
 extern Stream5IcmpConfig *s5_icmp_eval_config;
 extern Stream5IpConfig *s5_ip_eval_config;
 extern tSfPolicyUserContextId s5_config;
diff --git a/src/preprocessors/perf-base.c b/src/preprocessors/perf-base.c
index 8983774..e76ef1a 100644
--- a/src/preprocessors/perf-base.c
+++ b/src/preprocessors/perf-base.c
@@ -7,6 +7,9 @@
 ** Dan Roelker <droelker () sourcefire com>
 ** Marc Norton <mnorton () sourcefire com>
 **
+** SCTP bits Copyright (C) 2013
+** Joshua Kinard <kumba12345 () gmail com>
+**
 ** 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
@@ -40,6 +43,8 @@
 **              and the permonitor command has 'reset' and 'accrue' commands instead.(MAN)
 **    10.4.06 : Added UDP Session Stats (SAS)
 **    4.3.07  : Added stats for TCP sessions (SAS)
+**    8.27.11 : Added stats for SCTP sessions (JK)
+**    5.19.13 : Updated SCTP support for current code (JK)
 */
 
 #include <time.h>
@@ -180,6 +185,9 @@ int InitBaseStats(SFBASE *sfBase)
     sfBase->iNewUDPSessions = 0;
     sfBase->iDeletedUDPSessions = 0;
 
+    sfBase->iNewSCTPSessions = 0;
+    sfBase->iDeletedSCTPSessions = 0;
+
     //sfBase->iAttributeHosts = 0;
     //sfBase->iAttributeReloads = 0;
     sfBase->total_mpls_packets = 0;
@@ -189,6 +197,7 @@ int InitBaseStats(SFBASE *sfBase)
 
     sfBase->total_tcp_filtered_packets = 0;
     sfBase->total_udp_filtered_packets = 0;
+    sfBase->total_sctp_filtered_packets = 0;
 
     sfBase->frag3_mem_in_use = 0;
     sfBase->stream5_mem_in_use = 0;
@@ -358,7 +367,7 @@ void UpdateIPReassStats(SFBASE *sfBase, int len)
 }
 
 /**API to update stats for packets discarded due to
- * TCP/UDP port/service based filtering.
+ * TCP/UDP/SCTP port/service based filtering.
  *
  * @param sfBase - pointer to accumulated stats
  */
@@ -372,6 +381,9 @@ void UpdateFilteredPacketStats(SFBASE *sfBase, unsigned int proto)
         case IPPROTO_UDP:
             sfBase->total_udp_filtered_packets++;
             break;
+        case IPPROTO_SCTP:
+            sfBase->total_sctp_filtered_packets++;
+            break;
         default:
             //coding error
             ;
@@ -504,6 +516,51 @@ int RemoveUDPSession(SFBASE *sfBase)
 
 /*
 **  NAME
+**    AddSCTPSession
+**
+**  DESCRIPTION
+**    Add a session count
+**
+**  FORMAL INPUTS
+**    SFBASE * - ptr to update.
+**
+**  FORMAL OUTPUTS
+**    int - 0 is successful
+*/
+int AddSCTPSession(SFBASE *sfBase)
+{
+    sfBase->iTotalSCTPSessions++;
+    sfBase->iNewSCTPSessions++;
+
+    if(sfBase->iTotalSCTPSessions > sfBase->iMaxSCTPSessions)
+        sfBase->iMaxSCTPSessions = sfBase->iTotalSCTPSessions;
+
+    return 0;
+}
+
+/*
+**  NAME
+**    RemoveSCTPSession
+**
+**  DESCRIPTION
+**    Add a session count
+**
+**  FORMAL INPUTS
+**    SFBASE * - ptr to update.
+**
+**  FORMAL OUTPUTS
+**    int - 0 is successful
+*/
+
+int RemoveSCTPSession(SFBASE *sfBase)
+{
+    sfBase->iTotalSCTPSessions--;
+    sfBase->iDeletedSCTPSessions++;
+    return 0;
+}
+
+/*
+**  NAME
 **    ProcessBaseStats
 **
 **  DESCRIPTION
@@ -690,6 +747,17 @@ int GetEventsPerSecond(SFBASE *sfBase, SFBASE_STATS *sfBaseStats,
     sfBase->iNewUDPSessions = 0;
     sfBase->iDeletedUDPSessions = 0;
 
+    sfBaseStats->total_sctp_sessions = sfBase->iTotalSCTPSessions;
+    sfBaseStats->max_sctp_sessions = sfBase->iMaxSCTPSessions;
+    sfBaseStats->deleted_sctp_sessions_per_second =
+        (double)(sfBase->iDeletedSCTPSessions) / Systimes->realtime;
+
+    sfBaseStats->new_sctp_sessions_per_second =
+        (double)(sfBase->iNewSCTPSessions) / Systimes->realtime;
+
+    sfBase->iNewSCTPSessions = 0;
+    sfBase->iDeletedSCTPSessions = 0;
+
     sfBase->iMaxSessionsInterval = sfBase->iTotalSessions;
     sfBase->iMidStreamSessions = 0;
     sfBase->iClosedSessions = 0;
@@ -975,6 +1043,7 @@ int CalculateBasePerfStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats)
 
     sfBaseStats->total_tcp_filtered_packets = sfBase->total_tcp_filtered_packets;
     sfBaseStats->total_udp_filtered_packets = sfBase->total_udp_filtered_packets;
+    sfBaseStats->total_sctp_filtered_packets = sfBase->total_sctp_filtered_packets;
 
 #ifdef NORMALIZER
     {
@@ -1213,6 +1282,10 @@ int GetPktDropStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats)
  * max-udp-sessions
  * del-udp-sessions/sec (udp stream cache)
  * new-udp-sessions/sec (udp stream cache)
+ * sctp-sessions
+ * max-sctp-sessions
+ * del-sctp-sessions/sec (sctp stream cache)
+ * new-sctp-sessions/sec (sctp stream cache)
  * max-sessions, interval (tcp stream cache)
  * curr-tcp-sessions-initializing (tcp stream cache, of total-sessions open)
  * curr-tcp-sessions-established (tcp stream cache, of total-sessions open)
@@ -1335,6 +1408,12 @@ int LogBasePerfStats(SFBASE_STATS *sfBaseStats,  FILE * fh )
             sfBaseStats->total_udp_sessions,
             sfBaseStats->max_udp_sessions);
 
+    fprintf(fh, "%.3f,%.3f," CSVu64 CSVu64,
+            sfBaseStats->new_sctp_sessions_per_second,
+            sfBaseStats->deleted_sctp_sessions_per_second,
+            sfBaseStats->total_sctp_sessions,
+            sfBaseStats->max_sctp_sessions);
+
     fprintf(fh, CSVu64 CSVu64 CSVu64 CSVu64 "%.3f,%.3f,%.3f,%.3f,%.3f,",
             sfBaseStats->max_tcp_sessions_interval,
             sfBaseStats->curr_tcp_sessions_initializing,
@@ -1354,9 +1433,10 @@ int LogBasePerfStats(SFBASE_STATS *sfBaseStats,  FILE * fh )
             sfBaseStats->avg_bytes_per_mpls_packet,
             sfBaseStats->kpackets_per_sec_mpls.realtime);
 
-    fprintf(fh, CSVu64 CSVu64,
+    fprintf(fh, CSVu64 CSVu64 CSVu64,
             sfBaseStats->total_tcp_filtered_packets,
-            sfBaseStats->total_udp_filtered_packets);
+            sfBaseStats->total_udp_filtered_packets,
+            sfBaseStats->total_sctp_filtered_packets);
 
 #ifdef NORMALIZER
     for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ )
@@ -1584,6 +1664,7 @@ int DisplayBasePerfStatsConsole(SFBASE_STATS *sfBaseStats, int iFlags)
     LogMessage("Injected:    " STDu64 "\n", sfBaseStats->total_injected_packets);
     LogMessage("Pkts Filtered TCP:     " STDu64 "\n", sfBaseStats->total_tcp_filtered_packets);
     LogMessage("Pkts Filtered UDP:     " STDu64 "\n\n", sfBaseStats->total_udp_filtered_packets);
+    LogMessage("Pkts Filtered SCTP:    " STDu64 "\n\n", sfBaseStats->total_sctp_filtered_packets);
 
     LogMessage("Mbits/Sec:   %.3f (wire)\n",
             sfBaseStats->wire_mbits_per_sec.realtime);
@@ -1663,52 +1744,58 @@ int DisplayBasePerfStatsConsole(SFBASE_STATS *sfBaseStats, int iFlags)
     /*
     **  Shows the number of snort alerts per second.
     */
-    LogMessage("Alerts/Sec             :  %.3f\n",   sfBaseStats->alerts_per_second);
+    LogMessage("Alerts/Sec              :  %.3f\n",   sfBaseStats->alerts_per_second);
 
     /* Session estimation statistics */
-    LogMessage("Syns/Sec               :  %.3f\n", sfBaseStats->syns_per_second);
-    LogMessage("Syn-Acks/Sec           :  %.3f\n", sfBaseStats->synacks_per_second);
-    LogMessage("New Cached Sessions/Sec:  %.3f\n", sfBaseStats->new_sessions_per_second);
-    LogMessage("Midstream Sessions/Sec :  %.3f\n", sfBaseStats->tcp_sessions_midstream_per_second);
-    LogMessage("Cached Sessions Del/Sec:  %.3f\n", sfBaseStats->deleted_sessions_per_second);
-    LogMessage("Closed Sessions/Sec    :  %.3f\n", sfBaseStats->tcp_sessions_closed_per_second);
-    LogMessage("TimedOut Sessions/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_timedout_per_second);
-    LogMessage("Pruned Sessions/Sec    :  %.3f\n", sfBaseStats->tcp_sessions_pruned_per_second);
-    LogMessage("Dropped Async Ssns/Sec :  %.3f\n", sfBaseStats->tcp_sessions_dropped_async_per_second);
-
-    LogMessage("Current Cached Sessions:  " STDu64 "\n", sfBaseStats->total_sessions);
-    LogMessage("Sessions Initializing  :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_initializing);
-    LogMessage("Sessions Established   :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_established);
-    LogMessage("Sessions Closing       :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_closing);
-    LogMessage("Max Cached Sessions    :  " STDu64 "\n", sfBaseStats->max_sessions);
-    LogMessage("Max Sessions (interval):  " STDu64 "\n", sfBaseStats->max_tcp_sessions_interval);
+    LogMessage("Syns/Sec                :  %.3f\n", sfBaseStats->syns_per_second);
+    LogMessage("Syn-Acks/Sec            :  %.3f\n", sfBaseStats->synacks_per_second);
+    LogMessage("New Cached Sessions/Sec :  %.3f\n", sfBaseStats->new_sessions_per_second);
+    LogMessage("Midstream Sessions/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_midstream_per_second);
+    LogMessage("Cached Sessions Del/Sec :  %.3f\n", sfBaseStats->deleted_sessions_per_second);
+    LogMessage("Closed Sessions/Sec     :  %.3f\n", sfBaseStats->tcp_sessions_closed_per_second);
+    LogMessage("TimedOut Sessions/Sec   :  %.3f\n", sfBaseStats->tcp_sessions_timedout_per_second);
+    LogMessage("Pruned Sessions/Sec     :  %.3f\n", sfBaseStats->tcp_sessions_pruned_per_second);
+    LogMessage("Dropped Async Ssns/Sec  :  %.3f\n", sfBaseStats->tcp_sessions_dropped_async_per_second);
+
+    LogMessage("Current Cached Sessions :  " STDu64 "\n", sfBaseStats->total_sessions);
+    LogMessage("Sessions Initializing   :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_initializing);
+    LogMessage("Sessions Established    :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_established);
+    LogMessage("Sessions Closing        :  " STDu64 "\n", sfBaseStats->curr_tcp_sessions_closing);
+    LogMessage("Max Cached Sessions     :  " STDu64 "\n", sfBaseStats->max_sessions);
+    LogMessage("Max Sessions (interval) :  " STDu64 "\n", sfBaseStats->max_tcp_sessions_interval);
 
     /* more instrumentation for stream4/frag2 */
-    LogMessage("Stream Flushes/Sec     :  %.3f\n", sfBaseStats->stream_flushes_per_second);
-    LogMessage("Stream Cache Faults/Sec:  " STDu64 "\n", sfBaseStats->stream_faults);
-    LogMessage("Stream Cache Timeouts  :  " STDu64 "\n", sfBaseStats->stream_timeouts);
-
-    LogMessage("Frag Creates()s/Sec    :  %.3f\n", sfBaseStats->frag_creates_per_second);
-    LogMessage("Frag Completes()s/Sec  :  %.3f\n", sfBaseStats->frag_completes_per_second);
-    LogMessage("Frag Inserts()s/Sec    :  %.3f\n", sfBaseStats->frag_inserts_per_second);
-    LogMessage("Frag Deletes/Sec       :  %.3f\n", sfBaseStats->frag_deletes_per_second);
-    LogMessage("Frag AutoFrees/Sec     :  %.3f\n", sfBaseStats->frag_autofrees_per_second);
-    LogMessage("Frag Flushes/Sec       :  %.3f\n", sfBaseStats->frag_flushes_per_second);
-
-    LogMessage("Current Cached Frags   :  " STDu64 "\n", sfBaseStats->current_frags);
-    LogMessage("Max Cached Frags       :  " STDu64 "\n", sfBaseStats->max_frags);
-    LogMessage("Frag Timeouts          :  " STDu64 "\n", sfBaseStats->frag_timeouts);
-    LogMessage("Frag Faults            :  " STDu64 "\n\n", sfBaseStats->frag_faults);
-
-    LogMessage("New Cached UDP Ssns/Sec:  %.3f\n", sfBaseStats->new_udp_sessions_per_second);
-    LogMessage("Cached UDP Ssns Del/Sec:  %.3f\n", sfBaseStats->deleted_udp_sessions_per_second);
-
-    LogMessage("Current Cached UDP Ssns:  " STDu64 "\n", sfBaseStats->total_udp_sessions);
-    LogMessage("Max Cached UDP Ssns    :  " STDu64 "\n\n", sfBaseStats->max_udp_sessions);
-
-#ifdef TARGET_BASED
-    LogMessage("Attribute Table Hosts  :  " STDu64 "\n", sfBaseStats->current_attribute_hosts);
-    LogMessage("Attribute Table Reloads:  " STDu64 "\n\n", sfBaseStats->attribute_table_reloads);
+    LogMessage("Stream Flushes/Sec      :  %.3f\n", sfBaseStats->stream_flushes_per_second);
+    LogMessage("Stream Cache Faults/Sec :  " STDu64 "\n", sfBaseStats->stream_faults);
+    LogMessage("Stream Cache Timeouts   :  " STDu64 "\n", sfBaseStats->stream_timeouts);
+
+    LogMessage("Frag Creates()s/Sec     :  %.3f\n", sfBaseStats->frag_creates_per_second);
+    LogMessage("Frag Completes()s/Sec   :  %.3f\n", sfBaseStats->frag_completes_per_second);
+    LogMessage("Frag Inserts()s/Sec     :  %.3f\n", sfBaseStats->frag_inserts_per_second);
+    LogMessage("Frag Deletes/Sec        :  %.3f\n", sfBaseStats->frag_deletes_per_second);
+    LogMessage("Frag AutoFrees/Sec      :  %.3f\n", sfBaseStats->frag_autofrees_per_second);
+    LogMessage("Frag Flushes/Sec        :  %.3f\n", sfBaseStats->frag_flushes_per_second);
+
+    LogMessage("Current Cached Frags    :  " STDu64 "\n", sfBaseStats->current_frags);
+    LogMessage("Max Cached Frags        :  " STDu64 "\n", sfBaseStats->max_frags);
+    LogMessage("Frag Timeouts           :  " STDu64 "\n", sfBaseStats->frag_timeouts);
+    LogMessage("Frag Faults             :  " STDu64 "\n\n", sfBaseStats->frag_faults);
+
+    LogMessage("New Cached UDP Ssns/Sec :  %.3f\n", sfBaseStats->new_udp_sessions_per_second);
+    LogMessage("Cached UDP Ssns Del/Sec :  %.3f\n", sfBaseStats->deleted_udp_sessions_per_second);
+
+    LogMessage("Current Cached UDP Ssns :  " STDu64 "\n", sfBaseStats->total_udp_sessions);
+    LogMessage("Max Cached UDP Ssns     :  " STDu64 "\n\n", sfBaseStats->max_udp_sessions);
+
+    LogMessage("New Cached SCTP Ssns/Sec:  %.3f\n", sfBaseStats->new_sctp_sessions_per_second);
+    LogMessage("Cached SCTP Ssns Del/Sec:  %.3f\n", sfBaseStats->deleted_sctp_sessions_per_second);
+
+    LogMessage("Current Cached SCTP Ssns:  " STDu64 "\n", sfBaseStats->total_sctp_sessions);
+    LogMessage("Max Cached SCTP Ssns    :  " STDu64 "\n\n", sfBaseStats->max_sctp_sessions);
+
+    #ifdef TARGET_BASED
+    LogMessage("Attribute Table Hosts   :  " STDu64 "\n", sfBaseStats->current_attribute_hosts);
+    LogMessage("Attribute Table Reloads :  " STDu64 "\n\n", sfBaseStats->attribute_table_reloads);
 #endif
 
 #ifdef NORMALIZER
diff --git a/src/preprocessors/perf-base.h b/src/preprocessors/perf-base.h
index e08b2cf..45e4709 100644
--- a/src/preprocessors/perf-base.h
+++ b/src/preprocessors/perf-base.h
@@ -143,6 +143,11 @@ typedef struct _SFBASE
     uint64_t   iDeletedUDPSessions;
     uint64_t   iMaxUDPSessions;
 
+    uint64_t   iTotalSCTPSessions;
+    uint64_t   iNewSCTPSessions;
+    uint64_t   iDeletedSCTPSessions;
+    uint64_t   iMaxSCTPSessions;
+
     uint64_t   iMaxSessionsInterval;
     uint64_t   iMidStreamSessions;
     uint64_t   iClosedSessions;
@@ -160,11 +165,15 @@ typedef struct _SFBASE
     uint64_t   total_blocked_mpls_packets;
     uint64_t   total_blocked_mpls_bytes;
 
-    /**TCP packets ignored due to port/service filtering.*/
+    /* TCP packets ignored due to port/service filtering. */
     uint64_t   total_tcp_filtered_packets;
-    /**UDP packets ignored due to port/service filtering.*/
+
+    /* UDP packets ignored due to port/service filtering. */
     uint64_t   total_udp_filtered_packets;
 
+    /* SCTP packets ignored due to port/service filtering. */
+    uint64_t   total_sctp_filtered_packets;
+
     uint64_t   frag3_mem_in_use;
     uint64_t   stream5_mem_in_use;
 }  SFBASE;
@@ -245,6 +254,11 @@ typedef struct _SFBASE_STATS {
     double   deleted_udp_sessions_per_second;
     double   new_udp_sessions_per_second;
 
+    uint64_t   total_sctp_sessions;
+    uint64_t   max_sctp_sessions;
+    double   deleted_sctp_sessions_per_second;
+    double   new_sctp_sessions_per_second;
+
     uint64_t   max_tcp_sessions_interval;
     uint64_t   curr_tcp_sessions_initializing;
     uint64_t   curr_tcp_sessions_established;
@@ -265,11 +279,15 @@ typedef struct _SFBASE_STATS {
     SYSTIMES mpls_mbits_per_sec;
     int      avg_bytes_per_mpls_packet;
 
-    /**TCP packets ignored due to port/service filtering.*/
+    /* TCP packets ignored due to port/service filtering. */
     uint64_t   total_tcp_filtered_packets;
-    /**UDP packets ignored due to port/service filtering.*/
+
+    /* UDP packets ignored due to port/service filtering. */
     uint64_t   total_udp_filtered_packets;
 
+    /* SCTP packets ignored due to port/service filtering. */
+    uint64_t   total_sctp_filtered_packets;
+
     uint64_t   frag3_mem_in_use;
     uint64_t   stream5_mem_in_use;
 }  SFBASE_STATS;
@@ -286,6 +304,8 @@ int CloseStreamSession(SFBASE *sfBase, char flags);
 int RemoveStreamSession(SFBASE *sfBase);
 int AddUDPSession(SFBASE *sfBase);
 int RemoveUDPSession(SFBASE *sfBase);
+int AddSCTPSession(SFBASE *sfBase);
+int RemoveSCTPSession(SFBASE *sfBase);
 
 void UpdateWireStats(SFBASE *sfBase, int len, int dropped, int injected);
 void UpdateMPLSStats(SFBASE *sfBase, int len, int dropped);
diff --git a/src/preprocessors/perf-flow.c b/src/preprocessors/perf-flow.c
index 84020c7..c475217 100644
--- a/src/preprocessors/perf-flow.c
+++ b/src/preprocessors/perf-flow.c
@@ -7,6 +7,8 @@
 ** Copyright (C) 2002-2013 Sourcefire, Inc.
 ** Marc Norton <mnorton () sourcefire com>
 ** Dan Roelker <droelker () sourcefire com>
+** Joshua Kinard <kumba12345 () gmail com>
+**
 **
 ** NOTES
 **   4.10.02 - Initial Checkin.  Norton
@@ -31,14 +33,16 @@
 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 **
 **  DESCRIPTION
-**    The following subroutines track eand analyze the traffic flow
+**    The following subroutines track and analyze the traffic flow
 **  statistics.
 **
 **   PacketLen vs Packet Count
 **   TCP-Port vs Packet Count
 **   UDP-Port vs Packet Count
+**   SCTP-Port vs Packet Count
 **   TCP High<->High Port Count
 **   UDP High<->High Port Count
+**   SCTP High<->High Port Count
 **
 **
 */
@@ -101,6 +105,8 @@ int InitFlowStats(SFFLOW *sfFlow)
         sfFlow->portTcpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * SF_MAX_PORT);
         sfFlow->portUdpSrc = (uint64_t*)SnortAlloc(sizeof(uint64_t) * SF_MAX_PORT);
         sfFlow->portUdpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * SF_MAX_PORT);
+        sfFlow->portSctpSrc = (uint64_t*)SnortAlloc(sizeof(uint64_t) * SF_MAX_PORT);
+        sfFlow->portSctpDst = (uint64_t*)SnortAlloc(sizeof(uint64_t) * SF_MAX_PORT);
         sfFlow->typeIcmp = (uint64_t *)SnortAlloc(sizeof(uint64_t) * 256);
 
         first = 0;
@@ -112,6 +118,8 @@ int InitFlowStats(SFFLOW *sfFlow)
         memset(sfFlow->portTcpDst, 0, sizeof(uint64_t) * SF_MAX_PORT);
         memset(sfFlow->portUdpSrc, 0, sizeof(uint64_t) * SF_MAX_PORT);
         memset(sfFlow->portUdpDst, 0, sizeof(uint64_t) * SF_MAX_PORT);
+        memset(sfFlow->portSctpSrc, 0, sizeof(uint64_t) * SF_MAX_PORT);
+        memset(sfFlow->portSctpDst, 0, sizeof(uint64_t) * SF_MAX_PORT);
         memset(sfFlow->typeIcmp, 0, sizeof(uint64_t) * 256);
     }
 
@@ -124,6 +132,9 @@ int InitFlowStats(SFFLOW *sfFlow)
     sfFlow->portUdpHigh=0;
     sfFlow->portUdpTotal=0;
 
+    sfFlow->portSctpHigh=0;
+    sfFlow->portSctpTotal=0;
+
     sfFlow->typeIcmpTotal = 0;
 
     return 0;
@@ -180,6 +191,18 @@ void FreeFlowStats(SFFLOW *sfFlow)
         sfFlow->portUdpDst = NULL;
     }
 
+    if (sfFlow->portSctpSrc != NULL)
+    {
+        free(sfFlow->portSctpSrc);
+        sfFlow->portSctpSrc = NULL;
+    }
+
+    if (sfFlow->portSctpDst != NULL)
+    {
+        free(sfFlow->portSctpDst);
+        sfFlow->portSctpDst = NULL;
+    }
+
     if (sfFlow->typeIcmp != NULL)
     {
         free(sfFlow->typeIcmp);
@@ -255,7 +278,7 @@ int UpdateTCPFlowStatsEx(SFFLOW *sfFlow, int sport, int dport, int len)
 int UpdateUDPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len )
 {
     /*
-     * Track how much data on each port, and hihg<-> high port data
+     * Track how much data on each port, and high<->high port data
      */
     if( sport <  1024 && dport > 1023 ) //sfFlow->maxPortToTrack )
     {
@@ -294,6 +317,48 @@ int UpdateUDPFlowStatsEx(SFFLOW *sfFlow, int sport, int dport, int len )
     return UpdateUDPFlowStats( sfFlow, sport, dport, len );
 }
 
+int UpdateSCTPFlowStats(SFFLOW *sfFlow, int sport, int dport, int len )
+{
+    /*
+     * Track how much data on each port, and high<->high port data
+     */
+    if( sport <  1024 && dport > 1023 ) //sfFlow->maxPortToTrack )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+    }
+    else if( dport < 1024 && sport > 1023 ) //sfFlow->maxPortToTrack )
+    {
+        sfFlow->portSctpDst  [ dport ]+= len;
+    }
+    else if( sport < 1023 && dport < 1023 )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+        sfFlow->portSctpDst  [ dport ]+= len;
+    }
+    else if( sport > 1023 && dport > 1023 )
+    {
+        sfFlow->portSctpSrc  [ sport ]+= len;
+        sfFlow->portSctpDst  [ dport ]+= len;
+
+        sfFlow->portSctpHigh += len;
+    }
+
+    sfFlow->portSctpTotal += len;
+
+    return 0;
+}
+
+int UpdateSCTPFlowStatsEx(SFFLOW *sfFlow, int sport, int dport, int len )
+{
+    if(!(perfmon_config->perf_flags & SFPERF_FLOW))
+       return 1;
+
+    if (sfFlow == NULL)
+        return 1;
+
+    return UpdateSCTPFlowStats( sfFlow, sport, dport, len );
+}
+
 int UpdateICMPFlowStats(SFFLOW *sfFlow, int type, int len)
 {
     if(type < 256)
@@ -431,19 +496,22 @@ int ProcessFlowStats(SFFLOW *sfFlow)
     memset(&sfFlowStats, 0x00, sizeof(sfFlowStats));
 
     /*
-    **  Calculate the percentage of TCP, UDP and ICMP
+    **  Calculate the percentage of TCP, UDP, SCTP, and ICMP
     **  and other traffic that consisted in the stream.
     */
     sfFlowStats.trafficTCP = 100.0 * (double)(sfFlow->portTcpTotal) /
                  (double)(sfFlow->byteTotal);
     sfFlowStats.trafficUDP = 100.0 * (double)(sfFlow->portUdpTotal) /
                  (double)(sfFlow->byteTotal);
+    sfFlowStats.trafficSCTP = 100.0 * (double)(sfFlow->portSctpTotal) /
+                 (double)(sfFlow->byteTotal);
     sfFlowStats.trafficICMP = 100.0 * (double)(sfFlow->typeIcmpTotal) /
                  (double)(sfFlow->byteTotal);
     sfFlowStats.trafficOTHER = 100.0 *
                    (double)((double)sfFlow->byteTotal -
                    ((double)sfFlow->portTcpTotal +
                    (double)sfFlow->portUdpTotal +
+                   (double)sfFlow->portSctpTotal +
                    (double)sfFlow->typeIcmpTotal)) /
                    (double)sfFlow->byteTotal;
 
@@ -553,6 +621,47 @@ int ProcessFlowStats(SFFLOW *sfFlow)
     sfFlow->portUdpTotal=0;
 
     /*
+    **  Calculate SCTP port processing based on src, dst and
+    **  total distributions.
+    */
+    for (i = 0; i < perfmon_config->flow_max_port_to_track; i++)
+    {
+        tot = sfFlow->portSctpSrc[i]+sfFlow->portSctpDst[i];
+        if(!tot)
+        {
+            sfFlowStats.portflowSCTP.totperc[i] = 0;
+            continue;
+        }
+
+        totperc= 100.0 * tot / sfFlow->portSctpTotal;
+
+        if(totperc > .1)
+        {
+            srate =  100.0 * (double)(sfFlow->portSctpSrc[i]) / tot ;
+            drate =  100.0 * (double)(sfFlow->portSctpDst[i]) / tot ;
+
+            sfFlowStats.portflowSCTP.totperc[i]    = totperc;
+            sfFlowStats.portflowSCTP.sport_rate[i] = srate;
+            sfFlowStats.portflowSCTP.dport_rate[i] = drate;
+        }
+        else
+        {
+            sfFlowStats.portflowSCTP.totperc[i] = 0;
+        }
+
+        sfFlow->portSctpSrc[i] = sfFlow->portSctpDst[i] = 0;
+    }
+
+    sfFlowStats.portflowHighSCTP = 100.0 * sfFlow->portSctpHigh /
+                                  sfFlow->portSctpTotal;
+
+    /*
+    **  Reset counters for next go round
+    */
+    sfFlow->portSctpHigh=0;
+    sfFlow->portSctpTotal=0;
+
+    /*
     **  Calculate ICMP statistics
     */
     for(i=0;i<256;i++)
@@ -609,6 +718,7 @@ static int DisplayFlowStats(SFFLOW_STATS *sfFlowStats)
     LogMessage(    "--------------------------------------\n");
     LogMessage("TCP:   %.2f%%\n", sfFlowStats->trafficTCP);
     LogMessage("UDP:   %.2f%%\n", sfFlowStats->trafficUDP);
+    LogMessage("SCTP:  %.2f%%\n", sfFlowStats->trafficSCTP);
     LogMessage("ICMP:  %.2f%%\n", sfFlowStats->trafficICMP);
     LogMessage("OTHER: %.2f%%\n", sfFlowStats->trafficOTHER);
 
@@ -672,6 +782,28 @@ static int DisplayFlowStats(SFFLOW_STATS *sfFlowStats)
 
     LogMessage("\n");
     LogMessage("\n");
+    LogMessage("SCTP Port Flows\n");
+    LogMessage(    "--------------\n");
+    for(i=0;i<SF_MAX_PORT;i++)
+    {
+        if(sfFlowStats->portflowSCTP.totperc[i] &&
+           sfFlowStats->portflowSCTP.dport_rate[i]  )
+        {
+            LogMessage("Port[%d] %.2f%% of Total, Src: %6.2f%% Dst: %6.2f%%\n",
+                        i, sfFlowStats->portflowSCTP.totperc[i],
+                        sfFlowStats->portflowSCTP.sport_rate[i],
+                        sfFlowStats->portflowSCTP.dport_rate[i]);
+        }
+    }
+
+    if(sfFlowStats->portflowHighSCTP > .1)
+    {
+        LogMessage("Ports[High<->High]: %.2f%%\n",
+                sfFlowStats->portflowHighSCTP);
+    }
+
+    LogMessage("\n");
+    LogMessage("\n");
     LogMessage("ICMP Type Flows\n");
     LogMessage(    "---------------\n");
     for(i=0;i<256;i++)
@@ -706,9 +838,10 @@ static int DisplayFlowIPStats(SFFLOW *sfFlow)
             continue;
         sfip_raw_ntop(key->ipA.family, key->ipA.ip32, ipA, sizeof(ipA));
         sfip_raw_ntop(key->ipB.family, key->ipB.ip32, ipB, sizeof(ipB));
-        LogMessage("[%s <-> %s]: " STDu64 " bytes in " STDu64 " packets (%u, %u, %u)\n", ipA, ipB,
+        LogMessage("[%s <-> %s]: " STDu64 " bytes in " STDu64 " packets (%u, %u, %u, %u)\n", ipA, ipB,
                 stats->total_bytes, stats->total_packets, stats->stateChanges[SFS_STATE_TCP_ESTABLISHED],
-                stats->stateChanges[SFS_STATE_TCP_CLOSED], stats->stateChanges[SFS_STATE_UDP_CREATED]);
+                stats->stateChanges[SFS_STATE_TCP_CLOSED], stats->stateChanges[SFS_STATE_UDP_CREATED],
+                stats->stateChanges[SFS_STATE_SCTP_CREATED]);
         total += stats->total_packets;
     }
     LogMessage("Classified " STDu64 " packets.\n", total);
@@ -735,13 +868,15 @@ static int WriteFlowIPStats(SFFLOW *sfFlow, FILE *fp)
             continue;
         sfip_raw_ntop(key->ipA.family, key->ipA.ip32, ipA, sizeof(ipA));
         sfip_raw_ntop(key->ipB.family, key->ipB.ip32, ipB, sizeof(ipB));
-        fprintf(fp, "%s,%s," CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64
-                CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 "%u,%u,%u\n",
+        fprintf(fp, "%s,%s," CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64
+                CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 CSVu64 "%u,%u,%u\n",
                 ipA, ipB,
                 stats->trafficStats[SFS_TYPE_TCP].packets_AtoB, stats->trafficStats[SFS_TYPE_TCP].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_TCP].packets_BtoA, stats->trafficStats[SFS_TYPE_TCP].bytes_BtoA,
                 stats->trafficStats[SFS_TYPE_UDP].packets_AtoB, stats->trafficStats[SFS_TYPE_UDP].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_UDP].packets_BtoA, stats->trafficStats[SFS_TYPE_UDP].bytes_BtoA,
+                stats->trafficStats[SFS_TYPE_SCTP].packets_AtoB, stats->trafficStats[SFS_TYPE_SCTP].bytes_AtoB,
+                stats->trafficStats[SFS_TYPE_SCTP].packets_BtoA, stats->trafficStats[SFS_TYPE_SCTP].bytes_BtoA,
                 stats->trafficStats[SFS_TYPE_OTHER].packets_AtoB, stats->trafficStats[SFS_TYPE_OTHER].bytes_AtoB,
                 stats->trafficStats[SFS_TYPE_OTHER].packets_BtoA, stats->trafficStats[SFS_TYPE_OTHER].bytes_BtoA,
                 stats->stateChanges[SFS_STATE_TCP_ESTABLISHED], stats->stateChanges[SFS_STATE_TCP_CLOSED],
diff --git a/src/preprocessors/perf-flow.h b/src/preprocessors/perf-flow.h
index c7534ad..14f7d53 100644
--- a/src/preprocessors/perf-flow.h
+++ b/src/preprocessors/perf-flow.h
@@ -5,6 +5,7 @@
 ** Copyright (C) 2002-2013 Sourcefire, Inc.
 ** Marc Norton <mnorton () sourcefire com>
 ** Dan Roelker <droelker () sourcefire com>
+** Joshua Kinard <kumba12345 () gmail com>
 **
 **
 ** This program is free software; you can redistribute it and/or modify
@@ -38,15 +39,17 @@
 typedef enum {
     SFS_TYPE_TCP   = 0,
     SFS_TYPE_UDP   = 1,
-    SFS_TYPE_OTHER = 2,
-    SFS_TYPE_MAX   = 3
+    SFS_TYPE_SCTP  = 2,
+    SFS_TYPE_OTHER = 3,
+    SFS_TYPE_MAX   = 4
 } SFSType;
 
 typedef enum {
     SFS_STATE_TCP_ESTABLISHED = 0,
     SFS_STATE_TCP_CLOSED      = 1,
     SFS_STATE_UDP_CREATED     = 2,
-    SFS_STATE_MAX             = 3
+    SFS_STATE_SCTP_CREATED    = 3,
+    SFS_STATE_MAX             = 4
 } SFSState;
 
 typedef struct _portflow {
@@ -77,6 +80,8 @@ typedef struct _sfflow {
     uint64_t   *portTcpDst;
     uint64_t   *portUdpSrc;
     uint64_t   *portUdpDst;
+    uint64_t   *portSctpSrc;
+    uint64_t   *portSctpDst;
 
     uint64_t   *typeIcmp;
 
@@ -86,6 +91,9 @@ typedef struct _sfflow {
     uint64_t    portUdpHigh;
     uint64_t    portUdpTotal;
 
+    uint64_t    portSctpHigh;
+    uint64_t    portSctpTotal;
+
     uint64_t    typeIcmpTotal;
 
     SFXHASH     *ipMap;
@@ -97,6 +105,7 @@ typedef struct _sfflow_stats {
 
     double    trafficTCP;
     double    trafficUDP;
+    double    trafficSCTP;
     double    trafficICMP;
     double    trafficOTHER;
 
@@ -106,6 +115,9 @@ typedef struct _sfflow_stats {
     PORTFLOW  portflowUDP;
     double    portflowHighUDP;
 
+    PORTFLOW  portflowSCTP;
+    double    portflowHighSCTP;
+
     ICMPFLOW  flowICMP;
 
 
@@ -128,6 +140,7 @@ int ProcessFlowIPStats(SFFLOW *sfFlow, FILE *fh);
 */
 int UpdateUDPFlowStatsEx(SFFLOW *, int sport, int dport, int len);
 int UpdateTCPFlowStatsEx(SFFLOW *, int sport, int dport, int len);
+int UpdateSCTPFlowStatsEx(SFFLOW *, int sport, int dport, int len);
 int UpdateICMPFlowStatsEx(SFFLOW *, int type, int len);
 int UpdateFlowIPStats(SFFLOW *, snort_ip_p src_addr, snort_ip_p dst_addr, int len, SFSType type);
 int UpdateFlowIPState(SFFLOW *, snort_ip_p src_addr, snort_ip_p dst_addr, SFSState state);
diff --git a/src/preprocessors/portscan.c b/src/preprocessors/portscan.c
index ad46d2a..98dc326 100644
--- a/src/preprocessors/portscan.c
+++ b/src/preprocessors/portscan.c
@@ -53,7 +53,7 @@
 **
 **  The primary goal of this portscan detection engine is to catch nmap and
 **  variant scanners.  The engine tracks connection attempts on TCP, UDP,
-**  ICMP, and IP Protocols.  If there is a valid response, the connection
+**  ICMP, SCTP, and IP Protocols.  If there is a valid response, the connection
 **  is marked as valid.  If there is no response or a invalid response
 **  (TCP RST), then we track these attempts separately, so we know the
 **  number of invalid responses and the number of connection attempts that
@@ -189,6 +189,24 @@ static PS_ALERT_CONF g_udp_hi_sweep =     {30,3,3,10};
 static PS_ALERT_CONF g_udp_hi_dist_ps =   {200,3,200,10};
 
 /*
+**  SCTP alert configurations
+*/
+static PS_ALERT_CONF g_sctp_low_ps =       {0,5,25,5};
+static PS_ALERT_CONF g_sctp_low_decoy_ps = {0,15,50,30};
+static PS_ALERT_CONF g_sctp_low_sweep =    {0,5,5,15};
+static PS_ALERT_CONF g_sctp_low_dist_ps =  {0,15,50,15};
+
+static PS_ALERT_CONF g_sctp_med_ps =       {200,10,60,15};
+static PS_ALERT_CONF g_sctp_med_decoy_ps = {200,30,120,60};
+static PS_ALERT_CONF g_sctp_med_sweep =    {30,5,5,20};
+static PS_ALERT_CONF g_sctp_med_dist_ps =  {200,30,120,30};
+
+static PS_ALERT_CONF g_sctp_hi_ps =        {200,3,100,10};
+static PS_ALERT_CONF g_sctp_hi_decoy_ps =  {200,7,200,60};
+static PS_ALERT_CONF g_sctp_hi_sweep =     {30,3,3,10};
+static PS_ALERT_CONF g_sctp_hi_dist_ps =   {200,3,200,10};
+
+/*
 **  IP Protocol alert configurations
 */
 static PS_ALERT_CONF g_ip_low_ps =        {0,10,10,50};
@@ -438,6 +456,11 @@ static int ps_filter_ignore(PS_PKT *ps_pkt)
             return 1;
         }
     }
+    else if(p->sctph)
+    {
+        if(!(portscan_eval_config->detect_scans & PS_PROTO_SCTP))
+            return 1;
+    }
     else
     {
         if(!(portscan_eval_config->detect_scans & PS_PROTO_IP))
@@ -455,7 +478,7 @@ static int ps_filter_ignore(PS_PKT *ps_pkt)
     {
         reverse_pkt = 1;
     }
-    else if (p->udph && p->ssnptr &&
+    else if ((p->udph || p->sctph) && p->ssnptr &&
              stream_api && stream_api->version >= STREAM_API_VERSION5)
     {
         if (stream_api->get_packet_direction(p) & PKT_FROM_SERVER)
@@ -644,6 +667,17 @@ static int ps_get_proto(PS_PKT *ps_pkt, int *proto)
         }
     }
 
+    if (portscan_eval_config->detect_scans & PS_PROTO_SCTP)
+    {
+        if ((p->sctph != NULL) ||
+            ((p->icmph != NULL) && (p->icmph->type == ICMP_DEST_UNREACH) &&
+             (p->icmph->code == ICMP_PORT_UNREACH) && (p->orig_sctph != NULL)))
+        {
+            *proto = PS_PROTO_SCTP;
+            return 0;
+        }
+    }
+
     if (portscan_eval_config->detect_scans & PS_PROTO_IP)
     {
         if ((IPH_IS_VALID(p) && (p->icmph == NULL))
@@ -1148,6 +1182,95 @@ static int ps_tracker_update_udp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
     return 0;
 }
 
+static int ps_tracker_update_sctp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
+                                  PS_TRACKER *scanned)
+{
+    Packet  *p;
+    time_t  pkt_time;
+    snort_ip    cleared;
+    IP_CLEAR(cleared);
+
+    p = (Packet *)ps_pkt->pkt;
+    pkt_time = packet_timeofday();
+
+    if(p->icmph)
+    {
+        if(p->icmph->type == ICMP_DEST_UNREACH &&
+           p->icmph->code == ICMP_PORT_UNREACH)
+        {
+            if(scanned)
+            {
+                ps_proto_update(&scanned->proto,0,1,CLEARED,0,0);
+                scanned->priority_node = 1;
+            }
+
+            if(scanner)
+            {
+                ps_proto_update(&scanner->proto,0,1,CLEARED,0,0);
+                scanner->priority_node = 1;
+            }
+        }
+    }
+    else if(p->sctph)
+    {
+        if (stream_api && (stream_api->version >= STREAM_API_VERSION5) &&
+            p->ssnptr)
+        {
+            uint32_t direction = stream_api->get_packet_direction(p);
+
+#ifdef SUP_IP6
+            if (direction == PKT_FROM_CLIENT)
+            {
+                if(scanned)
+                {
+                    ps_proto_update(&scanned->proto,1,0,
+                                     GET_SRC_IP(p),p->dp, pkt_time);
+                }
+
+                if(scanner)
+                {
+                    ps_proto_update(&scanner->proto,1,0,
+                                     GET_DST_IP(p),p->dp, pkt_time);
+                }
+            }
+            else if (direction == PKT_FROM_SERVER)
+            {
+                if(scanned)
+                    ps_proto_update(&scanned->proto,-1,0,CLEARED,0,0);
+
+                if(scanner)
+                    ps_proto_update(&scanner->proto,-1,0,CLEARED,0,0);
+            }
+#else
+            if (direction == PKT_FROM_CLIENT)
+            {
+                if(scanned)
+                {
+                    ps_proto_update(&scanned->proto,1,0,
+                                     p->iph->ip_src.s_addr,p->dp, pkt_time);
+                }
+
+                if(scanner)
+                {
+                    ps_proto_update(&scanner->proto,1,0,
+                                     p->iph->ip_dst.s_addr,p->dp, pkt_time);
+                }
+            }
+            else if (direction == PKT_FROM_SERVER)
+            {
+                if(scanned)
+                    ps_proto_update(&scanned->proto,-1,0,0,0,0);
+
+                if(scanner)
+                    ps_proto_update(&scanner->proto,-1,0,0,0,0);
+            }
+#endif
+        }
+    }
+
+    return 0;
+}
+
 static int ps_tracker_update_icmp(PS_PKT *ps_pkt, PS_TRACKER *scanner,
                                   PS_TRACKER *scanned)
 {
@@ -1240,6 +1363,12 @@ static int ps_tracker_update(PS_PKT *ps_pkt, PS_TRACKER *scanner,
 
             break;
 
+        case PS_PROTO_SCTP:
+            if(ps_tracker_update_sctp(ps_pkt, scanner, scanned))
+                return -1;
+
+            break;
+
         case PS_PROTO_IP:
             if(ps_tracker_update_ip(ps_pkt, scanner, scanned))
                 return -1;
@@ -1624,6 +1753,77 @@ static int ps_alert_udp(PS_PROTO *scanner, PS_PROTO *scanned)
     return 0;
 }
 
+static int ps_alert_sctp(PS_PROTO *scanner, PS_PROTO *scanned)
+{
+    static PS_ALERT_CONF *one_to_one;
+    static PS_ALERT_CONF *one_to_one_decoy;
+    static PS_ALERT_CONF *one_to_many;
+    static PS_ALERT_CONF *many_to_one;
+
+    /*
+    ** Set the configurations depending on the sensitivity
+    ** level.
+    */
+    switch(portscan_eval_config->sense_level)
+    {
+        case PS_SENSE_HIGH:
+            one_to_one       = &g_sctp_hi_ps;
+            one_to_one_decoy = &g_sctp_hi_decoy_ps;
+            one_to_many      = &g_sctp_hi_sweep;
+            many_to_one      = &g_sctp_hi_dist_ps;
+
+            break;
+
+        case PS_SENSE_MEDIUM:
+            one_to_one       = &g_sctp_med_ps;
+            one_to_one_decoy = &g_sctp_med_decoy_ps;
+            one_to_many      = &g_sctp_med_sweep;
+            many_to_one      = &g_sctp_med_dist_ps;
+
+            break;
+
+        case PS_SENSE_LOW:
+            one_to_one       = &g_sctp_low_ps;
+            one_to_one_decoy = &g_sctp_low_decoy_ps;
+            one_to_many      = &g_sctp_low_sweep;
+            many_to_one      = &g_sctp_low_dist_ps;
+
+            break;
+
+        default:
+            return -1;
+    }
+
+    /*
+    **  Do detection on the different portscan types.
+    */
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_PORTSCAN) &&
+        ps_alert_one_to_one(scanner, scanned, one_to_one))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_DECOYSCAN) &&
+        ps_alert_one_to_one_decoy(scanner, scanned, one_to_one_decoy))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_PORTSWEEP) &&
+        ps_alert_one_to_many(scanner, scanned, one_to_many))
+    {
+        return 0;
+    }
+
+    if((portscan_eval_config->detect_scan_type & PS_TYPE_DISTPORTSCAN) &&
+        ps_alert_many_to_one(scanner, scanned, many_to_one))
+    {
+        return 0;
+    }
+
+    return 0;
+}
+
 static int ps_alert_icmp(PS_PROTO *scanner, PS_PROTO *scanned)
 {
     static PS_ALERT_CONF *one_to_many;
@@ -1702,6 +1902,11 @@ static int ps_tracker_alert(PS_PKT *ps_pkt, PS_TRACKER *scanner,
                           (scanned ? &scanned->proto : NULL));
             break;
 
+        case PS_PROTO_SCTP:
+            ps_alert_sctp((scanner ? &scanner->proto : NULL),
+                          (scanned ? &scanned->proto : NULL));
+            break;
+
         case PS_PROTO_IP:
             ps_alert_ip((scanner ? &scanner->proto : NULL),
                         (scanned ? &scanned->proto : NULL));
@@ -1823,6 +2028,12 @@ void ps_tracker_print(PS_TRACKER* ps_tracker)
         ps_proto_print(&ps_tracker->proto);
         proto_index++;
     }
+    if(portscan_eval_config->detect_scans & PS_PROTO_SCTP)
+    {
+        printf("    ** SCTP **\n");
+        ps_proto_print(&ps_tracker->proto);
+        proto_index++;
+    }
     if(portscan_eval_config->detect_scans & PS_PROTO_IP)
     {
         printf("    ** IP **\n");
diff --git a/src/preprocessors/portscan.h b/src/preprocessors/portscan.h
index 1f32e98..07bebce 100644
--- a/src/preprocessors/portscan.h
+++ b/src/preprocessors/portscan.h
@@ -103,9 +103,10 @@ typedef struct s_PS_PKT
 #define PS_PROTO_NONE        0x00
 #define PS_PROTO_TCP         0x01
 #define PS_PROTO_UDP         0x02
-#define PS_PROTO_ICMP        0x04
-#define PS_PROTO_IP          0x08
-#define PS_PROTO_ALL         0x0f
+#define PS_PROTO_SCTP        0x04
+#define PS_PROTO_ICMP        0x08
+#define PS_PROTO_IP          0x10
+#define PS_PROTO_ALL         0x1f
 
 #define PS_PROTO_OPEN_PORT   0x80
 
diff --git a/src/preprocessors/spp_frag3.c b/src/preprocessors/spp_frag3.c
index 0d4cb25..6c1c5e4 100644
--- a/src/preprocessors/spp_frag3.c
+++ b/src/preprocessors/spp_frag3.c
@@ -505,8 +505,8 @@ static void PrintFragKey(FRAGKEY *fkey)
 
     if(fkey)
     {
-        LogMessage("   sip: %s\n", FragIPToStr(fkey->sip, fkey->ipver));
-        LogMessage("   dip: %s\n", FragIPToStr(fkey->dip, fkey->ipver));
+        LogMessage("    sip: %s\n", FragIPToStr(fkey->sip, fkey->ipver));
+        LogMessage("    dip: %s\n", FragIPToStr(fkey->dip, fkey->ipver));
         LogMessage("     id: %d\n", fkey->id);
         LogMessage("  proto: 0x%X\n", fkey->proto);
 #ifdef MPLS
@@ -4820,6 +4820,11 @@ int fragGetApplicationProtocolId(Packet *p)
             src_port = p->sp;
             dst_port = p->dp;
             break;
+        case IPPROTO_SCTP:
+            ft->ipprotocol = protocolReferenceSCTP;
+            src_port = p->sp;
+            dst_port = p->dp;
+            break;
         case IPPROTO_ICMP:
             ft->ipprotocol = protocolReferenceICMP;
             break;
diff --git a/src/preprocessors/spp_perfmonitor.c b/src/preprocessors/spp_perfmonitor.c
index f8b3c73..00052c9 100644
--- a/src/preprocessors/spp_perfmonitor.c
+++ b/src/preprocessors/spp_perfmonitor.c
@@ -542,6 +542,14 @@ static void ProcessPerfMonitor(Packet *p, void *context)
             type = SFS_TYPE_UDP;
         }
         /*
+        *  SCTP Flow Stats
+        */
+        else if( p->sctph )
+        {
+            UpdateSCTPFlowStatsEx(&sfFlow, p->sp, p->dp, p->pkth->caplen);
+            type = SFS_TYPE_SCTP;
+        }
+        /*
         *  Get stats for ICMP packets
         */
         else if( p->icmph )
@@ -557,6 +565,8 @@ static void ProcessPerfMonitor(Packet *p, void *context)
             type = SFS_TYPE_TCP;
         else if (p->udph)
             type = SFS_TYPE_UDP;
+        else if (p->sctph)
+            type = SFS_TYPE_SCTP;
 
         UpdateFlowIPStats(&sfFlow, GET_SRC_IP(p), GET_DST_IP(p), p->pkth->caplen, type);
     }
diff --git a/src/preprocessors/spp_sfportscan.c b/src/preprocessors/spp_sfportscan.c
index 5861475..4407c14 100644
--- a/src/preprocessors/spp_sfportscan.c
+++ b/src/preprocessors/spp_sfportscan.c
@@ -30,7 +30,7 @@
 **    - User Configuration:  The following is a list of parameters that can
 **      be configured through the user interface:
 **
-**      proto  { tcp udp icmp ip all }
+**      proto  { tcp udp sctp icmp ip all }
 **      scan_type { portscan portsweep decoy_portscan distributed_portscan all }
 **      sense_level { high }    # high, medium, low
 **      watch_ip { }            # list of IPs, CIDR blocks
@@ -447,6 +447,7 @@ static int MakePortscanPkt(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type,
     {
         case PS_PROTO_TCP:
         case PS_PROTO_UDP:
+        case PS_PROTO_SCTP:
         case PS_PROTO_ICMP:
         case PS_PROTO_IP:
             if(MakeProtoInfo(proto, (u_char *)g_tmp_pkt->data, &ip_size))
@@ -635,6 +636,61 @@ static int PortscanAlertUdp(Packet *p, PS_PROTO *proto, int proto_type)
     return 0;
 }
 
+static int PortscanAlertSctp(Packet *p, PS_PROTO *proto, int proto_type)
+{
+    if(!proto)
+        return -1;
+
+    switch(proto->alerts)
+    {
+        case PS_ALERT_ONE_TO_ONE:
+            GeneratePSSnortEvent(p, GENERATOR_PSNG, PSNG_SCTP_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_DECOY:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_DECOY_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_DECOY_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_PORTSWEEP:
+           GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_PORTSWEEP, 0, 0, 3,
+                    PSNG_SCTP_PORTSWEEP_STR);
+            break;
+
+        case PS_ALERT_DISTRIBUTED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_DISTRIBUTED_PORTSCAN,
+                    0, 0, 3, PSNG_SCTP_DISTRIBUTED_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_FILTERED_PORTSCAN,0,0,3,
+                    PSNG_SCTP_FILTERED_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_ONE_TO_ONE_DECOY_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_FILTERED_DECOY_PORTSCAN,
+                    0,0,3, PSNG_SCTP_FILTERED_DECOY_PORTSCAN_STR);
+            break;
+
+        case PS_ALERT_PORTSWEEP_FILTERED:
+           GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_SCTP_PORTSWEEP_FILTERED,0,0,3,
+                    PSNG_SCTP_PORTSWEEP_FILTERED_STR);
+            break;
+
+        case PS_ALERT_DISTRIBUTED_FILTERED:
+            GeneratePSSnortEvent(p,GENERATOR_PSNG,
+                    PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN, 0, 0, 3,
+                    PSNG_SCTP_FILTERED_DISTRIBUTED_PORTSCAN_STR);
+            break;
+
+        default:
+            break;
+    }
+
+    return 0;
+}
+
 static int PortscanAlertIp(Packet *p, PS_PROTO *proto, int proto_type)
 {
     if(!proto)
@@ -750,6 +806,10 @@ static int PortscanAlert(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type)
                 PortscanAlertIcmp(g_tmp_pkt, proto, proto_type);
                 break;
 
+            case PS_PROTO_SCTP:
+                PortscanAlertSctp(g_tmp_pkt, proto, proto_type);
+                break;
+
             case PS_PROTO_IP:
                 PortscanAlertIp(g_tmp_pkt, proto, proto_type);
                 break;
@@ -848,6 +908,8 @@ static void ParseProtos(int *protos, char **savptr)
             *protos |= PS_PROTO_TCP;
         else if(!strcasecmp(pcTok, "udp"))
             *protos |= PS_PROTO_UDP;
+        else if(!strcasecmp(pcTok, "sctp"))
+            *protos |= PS_PROTO_SCTP;
         else if(!strcasecmp(pcTok, "icmp"))
             *protos |= PS_PROTO_ICMP;
         else if(!strcasecmp(pcTok, "ip"))
@@ -1041,6 +1103,7 @@ static void PrintPortscanConf(int detect_scans, int detect_scan_type,
         SnortSnprintf(buf, STD_BUF + 1, "    Detect Protocols:  ");
         if(detect_scans & PS_PROTO_TCP)  { sfsnprintfappend(buf, STD_BUF, "TCP ");  proto_cnt++; }
         if(detect_scans & PS_PROTO_UDP)  { sfsnprintfappend(buf, STD_BUF, "UDP ");  proto_cnt++; }
+        if(detect_scans & PS_PROTO_SCTP) { sfsnprintfappend(buf, STD_BUF, "SCTP "); proto_cnt++; }
         if(detect_scans & PS_PROTO_ICMP) { sfsnprintfappend(buf, STD_BUF, "ICMP "); proto_cnt++; }
         if(detect_scans & PS_PROTO_IP)   { sfsnprintfappend(buf, STD_BUF, "IP");    proto_cnt++; }
         LogMessage("%s\n", buf);
diff --git a/src/preprocessors/spp_stream5.c b/src/preprocessors/spp_stream5.c
index bbf1eeb..d40104d 100644
--- a/src/preprocessors/spp_stream5.c
+++ b/src/preprocessors/spp_stream5.c
@@ -53,6 +53,7 @@
 #include "snort_stream5_session.h"
 #include "snort_stream5_tcp.h"
 #include "snort_stream5_udp.h"
+#include "snort_stream5_sctp.h"
 #include "snort_stream5_icmp.h"
 #include "snort_stream5_ip.h"
 #include "checksum.h"
@@ -83,6 +84,7 @@
 PreprocStats s5PerfStats;
 extern PreprocStats s5TcpPerfStats;
 extern PreprocStats s5UdpPerfStats;
+extern PreprocStats s5SctpPerfStats;
 extern PreprocStats s5IcmpPerfStats;
 extern PreprocStats s5IpPerfStats;
 #endif
@@ -90,6 +92,7 @@ extern PreprocStats s5IpPerfStats;
 extern OptTreeNode *otn_tmp;
 extern Stream5SessionCache *tcp_lws_cache;
 extern Stream5SessionCache *udp_lws_cache;
+extern Stream5SessionCache *sctp_lws_cache;
 extern Stream5SessionCache *icmp_lws_cache;
 extern Stream5SessionCache *ip_lws_cache;
 
@@ -110,6 +113,7 @@ extern FlushConfig ignore_flush_policy_protocol[MAX_PROTOCOL_ORDINAL];
 #define S5_RIDICULOUS_MAX_SESSIONS 1024*1024 /* 1 million sessions */
 #define S5_DEFAULT_MAX_TCP_SESSIONS 262144 /* 256k TCP sessions by default */
 #define S5_DEFAULT_MAX_UDP_SESSIONS 131072 /* 128k UDP sessions by default */
+#define S5_DEFAULT_MAX_SCTP_SESSIONS 262144 /* 256k SCTP sessions by default */
 #define S5_DEFAULT_MAX_ICMP_SESSIONS 65536 /* 64k ICMP sessions by default */
 #define S5_DEFAULT_MAX_IP_SESSIONS   16384 /* 16k IP sessions by default */
 #define S5_MIN_PRUNE_LOG_MAX     1024      /* 1k packet data stored */
@@ -130,6 +134,7 @@ tSfPolicyUserContextId s5_config = NULL;
 Stream5GlobalConfig *s5_global_eval_config = NULL;
 Stream5TcpConfig *s5_tcp_eval_config = NULL;
 Stream5UdpConfig *s5_udp_eval_config = NULL;
+Stream5SctpConfig *s5_sctp_eval_config = NULL;
 Stream5IcmpConfig *s5_icmp_eval_config = NULL;
 Stream5IpConfig *s5_ip_eval_config = NULL;
 
@@ -138,7 +143,7 @@ uint32_t mem_in_use = 0;
 uint32_t firstPacketTime = 0;
 Stream5Stats s5stats;
 MemPool s5FlowMempool;
-static PoolCount s_tcp_sessions = 0, s_udp_sessions = 0;
+static PoolCount s_tcp_sessions = 0, s_udp_sessions = 0, s_sctp_sessions = 0;
 static PoolCount s_icmp_sessions = 0, s_ip_sessions = 0;
 static int s_proto_flags = 0;
 
@@ -153,6 +158,7 @@ static void Stream5GlobalInit(char *);
 static void Stream5ParseGlobalArgs(Stream5GlobalConfig *, char *);
 static void Stream5PolicyInitTcp(char *);
 static void Stream5PolicyInitUdp(char *);
+static void Stream5PolicyInitSctp(char *);
 static void Stream5PolicyInitIcmp(char *);
 static void Stream5PolicyInitIp(char *);
 static void Stream5CleanExit(int, void *);
@@ -172,6 +178,7 @@ tSfPolicyUserContextId s5_swap_config = NULL;
 static void Stream5GlobalReload(char *);
 static void Stream5TcpReload(char *);
 static void Stream5UdpReload(char *);
+static void Stream5SctpReload(char *);
 static void Stream5IcmpReload(char *);
 static void Stream5IpReload(char *);
 static int Stream5ReloadVerify(void);
@@ -439,6 +446,7 @@ void SetupStream5(void)
     RegisterPreprocessor("stream5_global", Stream5GlobalInit);
     RegisterPreprocessor("stream5_tcp", Stream5PolicyInitTcp);
     RegisterPreprocessor("stream5_udp", Stream5PolicyInitUdp);
+    RegisterPreprocessor("stream5_sctp", Stream5PolicyInitSctp);
     RegisterPreprocessor("stream5_icmp", Stream5PolicyInitIcmp);
     RegisterPreprocessor("stream5_ip", Stream5PolicyInitIp);
 #else
@@ -448,6 +456,8 @@ void SetupStream5(void)
                          Stream5TcpReload, NULL, NULL);
     RegisterPreprocessor("stream5_udp", Stream5PolicyInitUdp,
                          Stream5UdpReload, NULL, NULL);
+    RegisterPreprocessor("stream5_sctp", Stream5PolicyInitSctp,
+                         Stream5SctpReload, NULL, NULL);
     RegisterPreprocessor("stream5_icmp", Stream5PolicyInitIcmp,
                          Stream5IcmpReload, NULL, NULL);
     RegisterPreprocessor("stream5_ip", Stream5PolicyInitIp,
@@ -473,6 +483,7 @@ static void Stream5GlobalInit(char *args)
         RegisterPreprocessorProfile("s5", &s5PerfStats, 0, &totalPerfStats);
         RegisterPreprocessorProfile("s5tcp", &s5TcpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5udp", &s5UdpPerfStats, 1, &s5PerfStats);
+        RegisterPreprocessorProfile("s5sctp", &s5SctpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5icmp", &s5IcmpPerfStats, 1, &s5PerfStats);
         RegisterPreprocessorProfile("s5ip", &s5IpPerfStats, 1, &s5PerfStats);
 #endif
@@ -515,6 +526,8 @@ static void Stream5GlobalInit(char *args)
     pCurrentPolicyConfig->global_config->max_tcp_sessions = S5_DEFAULT_MAX_TCP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_udp_sessions = S5_TRACK_YES;
     pCurrentPolicyConfig->global_config->max_udp_sessions = S5_DEFAULT_MAX_UDP_SESSIONS;
+    pCurrentPolicyConfig->global_config->track_sctp_sessions = S5_TRACK_YES;
+    pCurrentPolicyConfig->global_config->max_sctp_sessions = S5_DEFAULT_MAX_SCTP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_icmp_sessions = S5_TRACK_NO;
     pCurrentPolicyConfig->global_config->max_icmp_sessions = S5_DEFAULT_MAX_ICMP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_ip_sessions = S5_TRACK_NO;
@@ -533,11 +546,12 @@ static void Stream5GlobalInit(char *args)
     if ((!pCurrentPolicyConfig->global_config->disabled) &&
         (pCurrentPolicyConfig->global_config->track_tcp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_udp_sessions == S5_TRACK_NO) &&
+        (pCurrentPolicyConfig->global_config->track_sctp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_icmp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_ip_sessions == S5_TRACK_NO))
     {
         FatalError("%s(%d) ==> Stream5 enabled, but not configured to track "
-                   "TCP, UDP, ICMP, or IP.\n", file_name, file_line);
+                   "TCP, UDP, SCTP, ICMP, or IP.\n", file_name, file_line);
     }
 
     if (policy_id != getDefaultPolicy())
@@ -546,6 +560,8 @@ static void Stream5GlobalInit(char *args)
             pDefaultPolicyConfig->global_config->max_tcp_sessions;
         pCurrentPolicyConfig->global_config->max_udp_sessions =
             pDefaultPolicyConfig->global_config->max_udp_sessions;
+        pCurrentPolicyConfig->global_config->max_sctp_sessions =
+            pDefaultPolicyConfig->global_config->max_sctp_sessions;
         pCurrentPolicyConfig->global_config->max_icmp_sessions =
             pDefaultPolicyConfig->global_config->max_icmp_sessions;
         pCurrentPolicyConfig->global_config->max_ip_sessions =
@@ -581,8 +597,9 @@ static void Stream5ParseGlobalArgs(Stream5GlobalConfig *config, char *args)
     char *endPtr = NULL;
 #define MAX_TCP 0x01
 #define MAX_UDP 0x02
-#define MAX_ICMP 0x04
-#define MAX_IP 0x08
+#define MAX_SCTP 0x04
+#define MAX_ICMP 0x08
+#define MAX_IP 0x10
     char max_set = 0;
 
     if (config == NULL)
@@ -710,6 +727,47 @@ static void Stream5ParseGlobalArgs(Stream5GlobalConfig *config, char *args)
                            file_name, file_line);
             }
         }
+        else if(!strcasecmp(stoks[0], "max_sctp"))
+        {
+            if (stoks[1])
+            {
+                config->max_sctp_sessions = strtoul(stoks[1], &endPtr, 10);
+                if (config->track_sctp_sessions == S5_TRACK_YES)
+                {
+                    if ((config->max_sctp_sessions > S5_RIDICULOUS_MAX_SESSIONS) ||
+                        (config->max_sctp_sessions == 0))
+                    {
+                        FatalError("%s(%d) => 'max_sctp %d' invalid: value must be "
+                                   "between 1 and %d sessions\n",
+                                   file_name, file_line,
+                                   config->max_sctp_sessions,
+                                   S5_RIDICULOUS_MAX_SESSIONS);
+                    }
+                }
+            }
+
+            if (!stoks[1] || (endPtr == &stoks[1][0]))
+            {
+                FatalError("%s(%d) => Invalid max_sctp in config file.  Requires integer parameter.\n",
+                           file_name, file_line);
+            }
+            max_set |= MAX_SCTP;
+        }
+        else if(!strcasecmp(stoks[0], "track_sctp"))
+        {
+            if (stoks[1])
+            {
+                if(!strcasecmp(stoks[1], "no"))
+                    config->track_sctp_sessions = S5_TRACK_NO;
+                else
+                    config->track_sctp_sessions = S5_TRACK_YES;
+            }
+            else
+            {
+                FatalError("%s(%d) => 'track_sctp' missing option\n",
+                           file_name, file_line);
+            }
+        }
         else if(!strcasecmp(stoks[0], "max_icmp"))
         {
             if (stoks[1])
@@ -914,6 +972,12 @@ static void Stream5PrintGlobalConfig(Stream5GlobalConfig *config)
     if (config->track_udp_sessions == S5_TRACK_YES)
         LogMessage("    Max UDP sessions: %u\n",
             config->max_udp_sessions);
+    LogMessage("    Track SCTP sessions: %s\n",
+        config->track_sctp_sessions == S5_TRACK_YES ?
+        "ACTIVE" : "INACTIVE");
+    if (config->track_sctp_sessions == S5_TRACK_YES)
+        LogMessage("    Max SCTP sessions: %u\n",
+            config->max_sctp_sessions);
     LogMessage("    Track ICMP sessions: %s\n",
         config->track_icmp_sessions == S5_TRACK_YES ?
         "ACTIVE" : "INACTIVE");
@@ -1029,6 +1093,44 @@ static void Stream5PolicyInitUdp(char *args)
     Stream5UdpPolicyInit(config->udp_config, args);
 }
 
+static void Stream5PolicyInitSctp(char *args)
+{
+    tSfPolicyId policy_id = getParserPolicy();
+    Stream5Config *config;
+
+    if (s5_config == NULL)
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+
+    sfPolicyUserPolicySet (s5_config, policy_id);
+    config = (Stream5Config *)sfPolicyUserDataGetCurrent(s5_config);
+
+    if ((config == NULL) || (config->global_config == NULL))
+    {
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+    }
+
+    if (!config->global_config->track_sctp_sessions)
+    {
+#ifdef SNORT_RELOAD
+        /* Return if we're reloading - the discrepancy will be handled in
+         * the reload verify */
+        if (s5_swap_config != NULL)
+#endif
+            return;
+    }
+
+    if (config->sctp_config == NULL)
+    {
+        config->sctp_config =
+            (Stream5SctpConfig *)SnortAlloc(sizeof(Stream5SctpConfig));
+
+        Stream5InitSctp(config->global_config);
+    }
+
+    /* Call the protocol specific initializer */
+    Stream5SctpPolicyInit(config->sctp_config, args);
+}
+
 static void Stream5PolicyInitIcmp(char *args)
 {
     tSfPolicyId policy_id = getParserPolicy();
@@ -1112,6 +1214,7 @@ static void Stream5Reset(int signal, void *foo)
 
     Stream5ResetTcp();
     Stream5ResetUdp();
+    Stream5ResetSctp();
     Stream5ResetIcmp();
     Stream5ResetIp();
 
@@ -1123,6 +1226,7 @@ static void Stream5ResetStats(int signal, void *foo)
     memset(&s5stats, 0, sizeof(s5stats));
     Stream5ResetTcpPrunes();
     Stream5ResetUdpPrunes();
+    Stream5ResetSctpPrunes();
     Stream5ResetIcmpPrunes();
     Stream5ResetIpPrunes();
 }
@@ -1131,12 +1235,14 @@ static void Stream5CleanExit(int signal, void *foo)
 {
     s5stats.tcp_prunes = Stream5GetTcpPrunes();
     s5stats.udp_prunes = Stream5GetUdpPrunes();
+    s5stats.sctp_prunes = Stream5GetSctpPrunes();
     s5stats.icmp_prunes = Stream5GetIcmpPrunes();
     s5stats.ip_prunes = Stream5GetIpPrunes();
 
     /* Clean up the hash tables for these */
     Stream5CleanTcp();
     Stream5CleanUdp();
+    Stream5CleanSctp();
     Stream5CleanIcmp();
     Stream5CleanIp();
 
@@ -1160,6 +1266,7 @@ static int Stream5VerifyConfigPolicy(
 
     int tcpNotConfigured = 0;
     int udpNotConfigured = 0;
+    int sctpNotConfigured = 0;
     int icmpNotConfigured = 0;
     int ipNotConfigured = 0;
     int proto_flags = 0;
@@ -1214,6 +1321,26 @@ static int Stream5VerifyConfigPolicy(
         }
     }
 
+    if (pPolicyConfig->global_config->track_sctp_sessions)
+    {
+        sctpNotConfigured =
+            !pPolicyConfig->global_config->max_sctp_sessions ||
+            Stream5VerifySctpConfig(pPolicyConfig->sctp_config, policyId);
+
+        if (sctpNotConfigured)
+        {
+            ErrorMessage(
+                "WARNING: Stream5 SCTP misconfigured (policy %u)\n", policyId);
+        }
+        else
+        {
+            if ( !(s_proto_flags & PROTO_BIT__SCTP) )
+                s_sctp_sessions += pPolicyConfig->global_config->max_sctp_sessions;
+
+            proto_flags |= PROTO_BIT__SCTP;
+        }
+    }
+
     if (pPolicyConfig->global_config->track_icmp_sessions)
     {
         icmpNotConfigured =
@@ -1254,7 +1381,7 @@ static int Stream5VerifyConfigPolicy(
         }
     }
 
-    if ( tcpNotConfigured || udpNotConfigured || icmpNotConfigured || ipNotConfigured )
+    if ( tcpNotConfigured || udpNotConfigured || sctpNotConfigured || icmpNotConfigured || ipNotConfigured )
     {
         FatalError("Stream5 not properly configured... exiting\n");
     }
@@ -1278,14 +1405,14 @@ static void Stream5VerifyConfig(void)
     if (s5_config == NULL)
         return;
 
-    s_tcp_sessions = s_udp_sessions = 0;
+    s_tcp_sessions = s_udp_sessions = s_sctp_sessions = 0;
     s_icmp_sessions = s_ip_sessions = 0;
 
     sfPolicyUserDataIterate (s5_config, Stream5VerifyConfigPolicy);
 
     defConfig = sfPolicyUserDataGet(s5_config, getDefaultPolicy());
 
-    total_sessions = s_tcp_sessions + s_udp_sessions
+    total_sessions = s_tcp_sessions + s_udp_sessions + s_sctp_sessions
                    + s_icmp_sessions + s_ip_sessions;
 
     if ( !total_sessions )
@@ -1303,6 +1430,12 @@ static void Stream5VerifyConfig(void)
         LogMessage("UDP tracking disabled, no UDP sessions allocated\n");
     }
 
+    if ( (defConfig->global_config->max_sctp_sessions > 0)
+        && (s_sctp_sessions == 0) )
+    {
+        LogMessage("SCTP tracking disabled, no SCTP sessions allocated\n");
+    }
+
     if ( (defConfig->global_config->max_icmp_sessions > 0)
         && (s_icmp_sessions == 0) )
     {
@@ -1347,15 +1480,18 @@ static void Stream5PrintStats(int exiting)
     LogMessage("            Total sessions: %u\n",
             s5stats.total_tcp_sessions +
             s5stats.total_udp_sessions +
+            s5stats.total_sctp_sessions +
             s5stats.total_icmp_sessions +
             s5stats.total_ip_sessions);
     LogMessage("              TCP sessions: %u\n", s5stats.total_tcp_sessions);
     LogMessage("              UDP sessions: %u\n", s5stats.total_udp_sessions);
+    LogMessage("             SCTP sessions: %u\n", s5stats.total_sctp_sessions);
     LogMessage("             ICMP sessions: %u\n", s5stats.total_icmp_sessions);
     LogMessage("               IP sessions: %u\n", s5stats.total_ip_sessions);
 
     LogMessage("                TCP Prunes: %u\n", Stream5GetTcpPrunes());
     LogMessage("                UDP Prunes: %u\n", Stream5GetUdpPrunes());
+    LogMessage("               SCTP Prunes: %u\n", Stream5GetSctpPrunes());
     LogMessage("               ICMP Prunes: %u\n", Stream5GetIcmpPrunes());
     LogMessage("                 IP Prunes: %u\n", Stream5GetIpPrunes());
     LogMessage("TCP StreamTrackers Created: %u\n",
@@ -1370,12 +1506,16 @@ static void Stream5PrintStats(int exiting)
     LogMessage("         TCP Segments Used: %u\n", s5stats.tcp_rebuilt_seqs_used);
     LogMessage("              TCP Discards: %u\n", s5stats.tcp_discards);
     LogMessage("                  TCP Gaps: %u\n", s5stats.tcp_gaps);
-    LogMessage("      UDP Sessions Created: %u\n",
-            s5stats.udp_sessions_created);
-    LogMessage("      UDP Sessions Deleted: %u\n",
-            s5stats.udp_sessions_released);
+    LogMessage("      UDP Sessions Created: %u\n", s5stats.udp_sessions_created);
+    LogMessage("      UDP Sessions Deleted: %u\n", s5stats.udp_sessions_released);
     LogMessage("              UDP Timeouts: %u\n", s5stats.udp_timeouts);
     LogMessage("              UDP Discards: %u\n", s5stats.udp_discards);
+    LogMessage("     SCTP Sessions Created: %u\n", s5stats.sctp_sessions_created);
+    LogMessage("     SCTP Sessions Deleted: %u\n", s5stats.sctp_sessions_released);
+    LogMessage("      SCTP Messages Queued: %u\n", s5stats.sctp_streammsgs_created);
+    LogMessage("    SCTP Messages Released: %u\n", s5stats.sctp_streammsgs_released);
+    LogMessage("             SCTP Timeouts: %u\n", s5stats.sctp_timeouts);
+    LogMessage("             SCTP Discards: %u\n", s5stats.sctp_discards);
     LogMessage("                    Events: %u\n", s5stats.events);
     LogMessage("           Internal Events: %u\n", s5stats.internalEvents);
     LogMessage("           TCP Port Filter\n");
@@ -1386,6 +1526,10 @@ static void Stream5PrintStats(int exiting)
     LogMessage("                   Dropped: %u\n", s5stats.udp_port_filter.dropped);
     LogMessage("                 Inspected: %u\n", s5stats.udp_port_filter.inspected);
     LogMessage("                   Tracked: %u\n", s5stats.udp_port_filter.session_tracked);
+    LogMessage("          SCTP Port Filter\n");
+    LogMessage("                   Dropped: %u\n", s5stats.sctp_port_filter.dropped);
+    LogMessage("                 Inspected: %u\n", s5stats.sctp_port_filter.inspected);
+    LogMessage("                   Tracked: %u\n", s5stats.sctp_port_filter.session_tracked);
 }
 
 /*
@@ -1412,7 +1556,7 @@ void Stream5Process(Packet *p, void *context)
 
     PREPROC_PROFILE_START(s5PerfStats);
 
-    /* Call individual TCP/UDP/ICMP/IP processing, per GET_IPH_PROTO(p) */
+    /* Call individual TCP/UDP/SCTP/ICMP/IP processing, per GET_IPH_PROTO(p) */
     switch(GET_IPH_PROTO(p))
     {
         case IPPROTO_TCP:
@@ -1449,6 +1593,23 @@ void Stream5Process(Packet *p, void *context)
 
             break;
 
+        case IPPROTO_SCTP:
+            {
+                Stream5SctpPolicy *policy = NULL;
+
+                lwssn = GetLWSession(sctp_lws_cache, p, &key);
+                if (lwssn != NULL)
+                    policy = (Stream5SctpPolicy *)lwssn->policy;
+
+                if (Stream5SetRuntimeConfiguration(lwssn, IPPROTO_SCTP) == -1)
+                    return;
+
+                if (s5_global_eval_config->track_sctp_sessions)
+                    Stream5ProcessSctp(p, lwssn, policy, &key);
+            }
+
+            break;
+
         case IPPROTO_ICMP:
             if (Stream5SetRuntimeConfiguration(NULL, IPPROTO_ICMP) != -1)
             {
@@ -1502,6 +1663,15 @@ static inline int IsEligible(Packet *p)
                  return 0;
         }
         break;
+        case IPPROTO_SCTP:
+        {
+             if(p->sctph == NULL)
+                 return 0;
+
+             if (p->error_flags & PKT_ERR_CKSUM_SCTP)
+                 return 0;
+        }
+        break;
         case IPPROTO_ICMP:
         {
              if(p->icmph == NULL)
@@ -1721,7 +1891,9 @@ static int Stream5AlertFlushStream(Packet *p)
         return 0;
     }
 
-    if ((ssn->protocol != IPPROTO_TCP) ||
+
+    if (((ssn->protocol != IPPROTO_TCP) ||
+         (ssn->protocol != IPPROTO_SCTP)) ||
         (p->packet_flags & PKT_REBUILT_STREAM))
     {
         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
@@ -1751,7 +1923,9 @@ static int Stream5ResponseFlushStream(Packet *p)
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    if ((ssn->protocol != IPPROTO_TCP) ||
+
+    if (((ssn->protocol != IPPROTO_TCP) ||
+         (ssn->protocol != IPPROTO_SCTP)) ||
         (p->packet_flags & PKT_REBUILT_STREAM))
     {
         DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE,
@@ -1810,6 +1984,9 @@ static int Stream5AddSessionAlert(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -1835,6 +2012,9 @@ static int Stream5LogSessionAlertExtraData(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -1859,6 +2039,9 @@ static int Stream5CheckSessionAlert(
 
     /* Don't need to do this for other protos because they don't
        do any reassembly. */
+    /* XXX: SCTP supports reassembly of DATA chunks, but Stream5 SCTP
+            is not complete yet.  For now, it mimics Stream5 UDP
+            functionality. */
     if ( GET_IPH_PROTO(p) != IPPROTO_TCP )
         return 0;
 
@@ -1944,17 +2127,14 @@ static void Stream5StopInspection(
         return;
 
     /* Flush any queued data on the client and/or server */
-    if (ssn->protocol == IPPROTO_TCP)
+    if ((ssn->protocol == IPPROTO_TCP) ||
+        (ssn->protocol == IPPROTO_SCTP))
     {
         if (ssn->ignore_direction & SSN_DIR_CLIENT)
-        {
             Stream5FlushClient(p, ssn);
-        }
 
         if (ssn->ignore_direction & SSN_DIR_SERVER)
-        {
             Stream5FlushServer(p, ssn);
-        }
     }
 
     /* TODO: Handle bytes/response parameters */
@@ -2008,6 +2188,9 @@ static void Stream5UpdateDirection(
         case IPPROTO_UDP:
             UdpUpdateDirection(ssn, dir, ip, port);
             break;
+        case IPPROTO_SCTP:
+            SctpUpdateDirection(ssn, dir, ip, port);
+            break;
         case IPPROTO_ICMP:
             //IcmpUpdateDirection(ssn, dir, ip, port);
             break;
@@ -2089,7 +2272,7 @@ static int Stream5GetRebuiltPackets(
 {
     Stream5LWSession *ssn = (Stream5LWSession*)p->ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 0;
 
     /* Only if this is a rebuilt packet */
@@ -2099,7 +2282,22 @@ static int Stream5GetRebuiltPackets(
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    return GetTcpRebuiltPackets(p, ssn, callback, userdata);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return GetTcpRebuiltPackets(p, ssn, callback, userdata);
+            break;
+
+        /* Not functional yet.
+         * case IPPROTO_SCTP:
+         *   return GetSctpRebuiltPackets(p, ssn, callback, userdata);
+         *   break;
+         */   
+
+        default:
+            return 0;
+            break;
+    }
 }
 
 static int Stream5GetStreamSegments(
@@ -2109,7 +2307,7 @@ static int Stream5GetStreamSegments(
 {
     Stream5LWSession *ssn = (Stream5LWSession*)p->ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return -1;
 
     /* Only if this is a rebuilt packet */
@@ -2119,7 +2317,22 @@ static int Stream5GetStreamSegments(
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return -1;
 
-    return GetTcpStreamSegments(p, ssn, callback, userdata);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return GetTcpStreamSegments(p, ssn, callback, userdata);
+            break;
+
+        /* Not functional yet
+         * case IPPROTO_SCTP:
+         *   return GetSctpStreamSegments(p, ssn, callback, userdata);
+         *   break;
+         */
+        
+        default:
+            return -1;
+            break;
+    }
 }
 
 static StreamFlowData *Stream5GetFlowData(Packet *p)
@@ -2147,39 +2360,83 @@ static char Stream5GetReassemblyDirection(void *ssnptr)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return SSN_DIR_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return SSN_DIR_NONE;
 
-    return Stream5GetReassemblyDirectionTcp(ssn);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetReassemblyDirectionTcp(ssn);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetReassemblyDirectionSctp(ssn);
+         *   break;
+         */   
+
+        default:
+            return SSN_DIR_NONE;
+            break;
+    }
 }
 
 static uint32_t Stream5GetFlushPoint(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return 0;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
 
-    return Stream5GetFlushPointTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetFlushPointTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetFlushPointSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 0;
+            break;
+    }
 }
 
 static void Stream5SetFlushPoint(void *ssnptr, char dir, uint32_t flush_point)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP))
+    if (!ssn)
         return;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return;
 
-    Stream5SetFlushPointTcp(ssn, dir, flush_point);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            Stream5SetFlushPointTcp(ssn, dir, flush_point);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   Stream5SetFlushPointSctp(ssn, dir, flush_point);
+         *   break;
+         */   
+
+        default:
+            break;
+    }
 }
 
 static char Stream5SetReassembly(void *ssnptr,
@@ -2189,65 +2446,140 @@ static char Stream5SetReassembly(void *ssnptr,
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 0;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 0;
+    
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5SetReassemblyTcp(ssn, flush_policy, dir, flags);
+            break;
 
-    return Stream5SetReassemblyTcp(ssn, flush_policy, dir, flags);
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5SetReassemblySctp(ssn, flush_policy, dir, flags);
+         *   break;
+         */   
+
+        default:
+            return 0;
+            break;
+    }
 }
 
 static char Stream5GetReassemblyFlushPolicy(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return STREAM_FLPOLICY_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return STREAM_FLPOLICY_NONE;
 
-    return Stream5GetReassemblyFlushPolicyTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5GetReassemblyFlushPolicyTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5GetReassemblyFlushPolicySctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return STREAM_FLPOLICY_NONE;
+            break;
+    }
 }
 
 static char Stream5IsStreamSequenced(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 1;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 1;
 
-    return Stream5IsStreamSequencedTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5IsStreamSequencedTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5IsStreamSequencedSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 1;
+            break;
+    }
 }
 
 static int Stream5MissingInReassembled(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return SSN_MISSING_NONE;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return SSN_MISSING_NONE;
 
-    return Stream5MissingInReassembledTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5MissingInReassembledTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5MissingInReassembledSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return SSN_MISSING_NONE;
+            break;
+    }
 }
 
 static char Stream5PacketsMissing(void *ssnptr, char dir)
 {
     Stream5LWSession *ssn = (Stream5LWSession *)ssnptr;
 
-    if (!ssn || ssn->protocol != IPPROTO_TCP)
+    if (!ssn)
         return 1;
 
     if (Stream5SetRuntimeConfiguration(ssn, ssn->protocol) == -1)
         return 1;
 
-    return Stream5PacketsMissingTcp(ssn, dir);
+    switch (ssn->protocol)
+    {
+        case IPPROTO_TCP:
+            return Stream5PacketsMissingTcp(ssn, dir);
+            break;
+
+        /* Not functional yet.   
+         * case IPPROTO_SCTP:
+         *   return Stream5PacketsMissingSctp(ssn, dir);
+         *   break;
+         */   
+
+        default:
+            return 1;
+            break;
+    }
 }
 
 static uint16_t s5GetPreprocessorStatusBit(void)
@@ -2272,11 +2604,18 @@ static void s5SetPortFilterStatus(
         case IPPROTO_TCP:
             s5TcpSetPortFilterStatus(port, status, policyId, parsing);
             break;
+
         case IPPROTO_UDP:
             s5UdpSetPortFilterStatus(port, status, policyId, parsing);
             break;
+
+        case IPPROTO_SCTP:
+            s5SctpSetPortFilterStatus(port, status, policyId, parsing);
+            break;
+
         case IPPROTO_ICMP:
             break;
+
         default:
             break;
     }
@@ -2366,6 +2705,9 @@ void Stream5SetIPProtocol(Stream5LWSession *lwssn)
     case IPPROTO_UDP:
         lwssn->ipprotocol = protocolReferenceUDP;
         break;
+    case IPPROTO_SCTP:
+        lwssn->ipprotocol = protocolReferenceSCTP;
+        break;
     case IPPROTO_ICMP:
         lwssn->ipprotocol = protocolReferenceICMP;
         break;
@@ -2428,7 +2770,8 @@ static void s5InitServiceFilterStatus(void)
         {
             RuleTreeNode *rtn = getRtnFromOtn(otn, policyId);
 
-            if (rtn && (rtn->proto == IPPROTO_TCP))
+            if (rtn && ((rtn->proto == IPPROTO_TCP) ||
+                        (rtn->proto == IPPROTO_SCTP)))
             {
                 unsigned int svc_idx;
 
@@ -2623,6 +2966,18 @@ int isPacketFilterDiscard(
 
             pPortFilterStats = &s5stats.udp_port_filter;
             break;
+
+        case IPPROTO_SCTP:
+            if ((s5_global_eval_config != NULL) &&
+                s5_global_eval_config->track_sctp_sessions)
+            {
+                action = s5SctpGetPortFilterStatus(p->sp, policy_id, 0) |
+                    s5SctpGetPortFilterStatus(p->dp, policy_id, 0);
+            }
+
+            pPortFilterStats = &s5stats.sctp_port_filter;
+            break;
+
         default:
             return PORT_MONITOR_PACKET_PROCESS;
     }
@@ -2770,6 +3125,8 @@ static void Stream5GlobalReload(char *args)
     pCurrentPolicyConfig->global_config->max_tcp_sessions = S5_DEFAULT_MAX_TCP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_udp_sessions = S5_TRACK_YES;
     pCurrentPolicyConfig->global_config->max_udp_sessions = S5_DEFAULT_MAX_UDP_SESSIONS;
+    pCurrentPolicyConfig->global_config->track_sctp_sessions = S5_TRACK_YES;
+    pCurrentPolicyConfig->global_config->max_sctp_sessions = S5_DEFAULT_MAX_SCTP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_icmp_sessions = S5_TRACK_NO;
     pCurrentPolicyConfig->global_config->max_icmp_sessions = S5_DEFAULT_MAX_ICMP_SESSIONS;
     pCurrentPolicyConfig->global_config->track_ip_sessions = S5_TRACK_NO;
@@ -2788,11 +3145,12 @@ static void Stream5GlobalReload(char *args)
     if ((!pCurrentPolicyConfig->global_config->disabled) &&
         (pCurrentPolicyConfig->global_config->track_tcp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_udp_sessions == S5_TRACK_NO) &&
+        (pCurrentPolicyConfig->global_config->track_sctp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_icmp_sessions == S5_TRACK_NO) &&
         (pCurrentPolicyConfig->global_config->track_ip_sessions == S5_TRACK_NO))
     {
         FatalError("%s(%d) ==> Stream5 enabled, but not configured to track "
-                   "TCP, UDP, ICMP, or IP.\n", file_name, file_line);
+                   "TCP, UDP, SCTP, ICMP, or IP.\n", file_name, file_line);
     }
 
     if (policy_id != getDefaultPolicy())
@@ -2801,6 +3159,8 @@ static void Stream5GlobalReload(char *args)
             pDefaultPolicyConfig->global_config->max_tcp_sessions;
         pCurrentPolicyConfig->global_config->max_udp_sessions =
             pDefaultPolicyConfig->global_config->max_udp_sessions;
+        pCurrentPolicyConfig->global_config->max_sctp_sessions =
+            pDefaultPolicyConfig->global_config->max_sctp_sessions;
         pCurrentPolicyConfig->global_config->max_icmp_sessions =
             pDefaultPolicyConfig->global_config->max_icmp_sessions;
         pCurrentPolicyConfig->global_config->max_ip_sessions =
@@ -2869,6 +3229,28 @@ static void Stream5UdpReload(char *args)
     Stream5UdpPolicyInit(config->udp_config, args);
 }
 
+static void Stream5SctpReload(char *args)
+{
+    tSfPolicyId policy_id = getParserPolicy();
+    Stream5Config *config;
+
+    if (s5_swap_config == NULL)
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+
+    config = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policy_id);
+
+    if ((config == NULL) || (config->global_config == NULL))
+    {
+        FatalError("Tried to config stream5 SCTP policy without global config!\n");
+    }
+
+    if (config->sctp_config == NULL)
+        config->sctp_config = (Stream5SctpConfig *)SnortAlloc(sizeof(Stream5SctpConfig));
+
+    /* Call the protocol specific initializer */
+    Stream5SctpPolicyInit(config->sctp_config, args);
+}
+
 static void Stream5IcmpReload(char *args)
 {
     tSfPolicyId policy_id = getParserPolicy();
@@ -2967,6 +3349,7 @@ static int Stream5ReloadVerifyPolicy(
     Stream5Config *sc = (Stream5Config *)sfPolicyUserDataGet(s5_swap_config, policyId);
     int tcpNotConfigured = 0;
     int udpNotConfigured = 0;
+    int sctpNotConfigured = 0;
     int icmpNotConfigured = 0;
     int ipNotConfigured = 0;
     int proto_flags = 0;
@@ -2983,10 +3366,11 @@ static int Stream5ReloadVerifyPolicy(
 
         if ((sc->global_config->track_tcp_sessions != cc->global_config->track_tcp_sessions) ||
                 (sc->global_config->track_udp_sessions != cc->global_config->track_udp_sessions) ||
+                (sc->global_config->track_sctp_sessions != cc->global_config->track_sctp_sessions) ||
                 (sc->global_config->track_icmp_sessions != cc->global_config->track_icmp_sessions) ||
                 (sc->global_config->track_ip_sessions != cc->global_config->track_ip_sessions))
         {
-            ErrorMessage("Stream5 Reload: Changing tracking of TCP, UDP ICMP, or IP "
+            ErrorMessage("Stream5 Reload: Changing tracking of TCP, UDP, SCTP, ICMP, or IP "
                     "sessions requires a restart.\n");
             Stream5FreeConfigs(s5_swap_config);
             s5_swap_config = NULL;
@@ -3017,6 +3401,14 @@ static int Stream5ReloadVerifyPolicy(
             return -1;
         }
 
+        if (sc->global_config->max_sctp_sessions != cc->global_config->max_sctp_sessions)
+        {
+            ErrorMessage("Stream5 Reload: Changing \"max_sctp\" requires a restart.\n");
+            Stream5FreeConfigs(s5_swap_config);
+            s5_swap_config = NULL;
+            return -1;
+        }
+
         if (cc->global_config->max_icmp_sessions != sc->global_config->max_icmp_sessions)
         {
             ErrorMessage("Stream5 Reload: Changing \"max_icmp\" requires a restart.\n");
@@ -3075,6 +3467,25 @@ static int Stream5ReloadVerifyPolicy(
         }
     }
 
+    if (sc->global_config->track_sctp_sessions)
+    {
+        sctpNotConfigured =
+            !sc->global_config->max_sctp_sessions ||
+            Stream5VerifySctpConfig(sc->sctp_config, policyId);
+
+        if (sctpNotConfigured)
+        {
+            ErrorMessage("WARNING: Stream5 SCTP misconfigured\n");
+        }
+        else
+        {
+            if ( !(s_proto_flags & PROTO_BIT__SCTP) )
+                s_sctp_sessions += sc->global_config->max_sctp_sessions;
+
+            proto_flags |= PROTO_BIT__SCTP;
+        }
+    }
+
     if (sc->global_config->track_icmp_sessions)
     {
         icmpNotConfigured =
diff --git a/src/preprocessors/stream_api.h b/src/preprocessors/stream_api.h
index 9932152..7efadef 100644
--- a/src/preprocessors/stream_api.h
+++ b/src/preprocessors/stream_api.h
@@ -23,7 +23,7 @@
 /* stream_api.h
  *
  * Purpose: Definition of the StreamAPI.  To be used as a common interface
- *          for TCP (and later UDP & ICMP) Stream access for other
+ *          for TCP (and later UDP, SCTP, & ICMP) Stream access for other
  *          preprocessors and detection plugins.
  *
  * Arguments:
diff --git a/src/rule_option_types.h b/src/rule_option_types.h
index e9841ac..a7dccfa 100644
--- a/src/rule_option_types.h
+++ b/src/rule_option_types.h
@@ -64,13 +64,17 @@ typedef enum _option_type_t
     RULE_OPTION_TYPE_TCP_SEQ,
     RULE_OPTION_TYPE_TCP_WIN,
     RULE_OPTION_TYPE_TTL,
-    RULE_OPTION_TYPE_URILEN
+    RULE_OPTION_TYPE_URILEN,
 #ifdef DYNAMIC_PLUGIN
-    ,
     RULE_OPTION_TYPE_HDR_OPT_CHECK,
     RULE_OPTION_TYPE_PREPROCESSOR,
-    RULE_OPTION_TYPE_DYNAMIC
+    RULE_OPTION_TYPE_DYNAMIC,
 #endif
+    RULE_OPTION_TYPE_SCTP_CHUNK_TYPE,
+    RULE_OPTION_TYPE_SCTP_CHUNK_FLAGS,
+    RULE_OPTION_TYPE_SCTP_CHUNK_FIELD,
+    RULE_OPTION_TYPE_SCTP_NUM_CHUNKS,
+    RULE_OPTION_TYPE_SCTP_CAUSE_CODE,
 } option_type_t;
 
 #endif /* RULE_OPTION_TYPES__H */
diff --git a/src/sf_protocols.h b/src/sf_protocols.h
index ca89f95..36a4622 100644
--- a/src/sf_protocols.h
+++ b/src/sf_protocols.h
@@ -35,6 +35,8 @@ typedef enum {
     PROTO_TCP,        // DecodeTCP
                       // DecodeTCPOptions - handled with TCP
 
+    PROTO_SCTP,       // DecodeSCTP
+
     PROTO_IP6,        // DecodeIPV6
                       // DecodeIPV6Extensions - nothing to do here, calls below
     PROTO_IP6_HOP_OPTS,  // DecodeIPV6Options - ip6 hop, dst, rte, and frag exts
diff --git a/src/sfutil/Makefile.am b/src/sfutil/Makefile.am
index 2432f72..6e95bad 100644
--- a/src/sfutil/Makefile.am
+++ b/src/sfutil/Makefile.am
@@ -50,6 +50,7 @@ libsfutil_a_SOURCES = \
     sf_base64decode.c sf_base64decode.h \
     Unified2_common.h \
     sf_seqnums.h \
+    adler32.c adler32.h \
     $(INTEL_SOFT_CPM_SOURCES)
 
 INCLUDES = @INCLUDES@
diff --git a/src/sfutil/Makefile.in b/src/sfutil/Makefile.in
index 2ee8565..ae6e19d 100644
--- a/src/sfutil/Makefile.in
+++ b/src/sfutil/Makefile.in
@@ -85,7 +85,7 @@ am__libsfutil_a_SOURCES_DIST = sfghash.c sfghash.h sfhashfcn.c \
 	sfrf.h strvec.c strvec.h sf_email_attach_decode.c \
 	sf_email_attach_decode.h sf_base64decode.c sf_base64decode.h \
 	Unified2_common.h sf_seqnums.h intel-soft-cpm.c \
-	intel-soft-cpm.h
+	intel-soft-cpm.h adler32.c adler32.h
 @HAVE_INTEL_SOFT_CPM_TRUE@am__objects_1 = intel-soft-cpm.$(OBJEXT)
 am_libsfutil_a_OBJECTS = sfghash.$(OBJEXT) sfhashfcn.$(OBJEXT) \
 	sflsq.$(OBJEXT) sfmemcap.$(OBJEXT) sfthd.$(OBJEXT) \
@@ -102,6 +102,7 @@ am_libsfutil_a_OBJECTS = sfghash.$(OBJEXT) sfhashfcn.$(OBJEXT) \
 	sfPolicy.$(OBJEXT) sfPolicyUserData.$(OBJEXT) \
 	sfActionQueue.$(OBJEXT) sfrf.$(OBJEXT) strvec.$(OBJEXT) \
 	sf_email_attach_decode.$(OBJEXT) sf_base64decode.$(OBJEXT) \
+	adler32.$(OBJEXT) \
 	$(am__objects_1)
 libsfutil_a_OBJECTS = $(am_libsfutil_a_OBJECTS)
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
@@ -304,6 +305,7 @@ libsfutil_a_SOURCES = \
     sf_base64decode.c sf_base64decode.h \
     Unified2_common.h \
     sf_seqnums.h \
+    adler32.h adler32.c \
     $(INTEL_SOFT_CPM_SOURCES)
 
 all: all-am
diff --git a/src/sfutil/adler32.c b/src/sfutil/adler32.c
new file mode 100644
index 0000000..adcae1c
--- /dev/null
+++ b/src/sfutil/adler32.c
@@ -0,0 +1,59 @@
+/* adler32.c
+ * Compute the Adler32 checksum (RFC 1950)
+ * Based on code from RFC 1950 (Chapter 9. Appendix: Sample code)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald () wireshark org>
+ * Original Copyright 1998 Gerald Combs
+ *                    2003 Tomas Kukosa
+ */
+
+#include <string.h>
+
+#include "adler32.h"
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+
+/*--- update_adler32 --------------------------------------------------------*/
+unsigned long update_adler32(unsigned long adler, const unsigned char *buf, int len)
+{
+  unsigned long s1 = adler & 0xffff;
+  unsigned long s2 = (adler >> 16) & 0xffff;
+  int n;
+
+  for (n = 0; n < len; n++) {
+    s1 = (s1 + buf[n]) % BASE;
+    s2 = (s2 + s1)     % BASE;
+  }
+  return (s2 << 16) + s1;
+}
+
+/*--- adler32 ---------------------------------------------------------------*/
+unsigned long adler32_bytes(const unsigned char *buf, int len)
+{
+  return update_adler32(1L, buf, len);
+}
+
+/*--- adler32_str -----------------------------------------------------------*/
+unsigned long adler32_str(const char *buf)
+{
+  return update_adler32(1L, (const unsigned char*)buf, (int)strlen(buf));
+}
+
+/*---------------------------------------------------------------------------*/
diff --git a/src/sfutil/adler32.h b/src/sfutil/adler32.h
new file mode 100644
index 0000000..81deb98
--- /dev/null
+++ b/src/sfutil/adler32.h
@@ -0,0 +1,41 @@
+/* adler32.h
+ * Compute the Adler32 checksum (RFC 1950)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald () wireshark org>
+ * Original Copyright 1998 Gerald Combs
+ *                    2003 Tomas Kukosa
+ */
+
+#ifndef ADLER32_H
+#define ADLER32_H
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+unsigned long update_adler32(unsigned long adler, const unsigned char *buf, int len);
+unsigned long adler32_bytes(const unsigned char *buf, int len);
+unsigned long adler32_str(const char *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* ADLER32_H */
diff --git a/src/sfutil/crc32.h b/src/sfutil/crc32.h
new file mode 100644
index 0000000..9629223
--- /dev/null
+++ b/src/sfutil/crc32.h
@@ -0,0 +1,129 @@
+/* crc32.c
+ * CRC-32 routine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Borrwed from Wireshark.  Hey, it's GPLv2!
+ * Original by Gerald Combs <gerald () wireshark org>
+ * Original Copyright 1998 Gerald Combs
+ *
+ * Credits:
+ * Table from Solomon Peachy
+ * Routine from Chris Waters
+ */
+
+#ifndef _CRC32_H_
+#define _CRC32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+/*****************************************************************/
+/*                                                               */
+/* CRC32C LOOKUP TABLE                                           */
+/* +++================                                           */
+/* The following CRC lookup table was generated automagically    */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
+/* Program V1.0 using the following model parameters:            */
+/*                                                               */
+/*    Width   : 4 bytes.                                         */
+/*    Poly    : 0x1EDC6F41L                                      */
+/*    Reverse : TRUE.                                            */
+/*                                                               */
+/* For more information on the Rocksoft^tm Model CRC Algorithm,  */
+/* see the document titled "A Painless Guide to CRC Error        */
+/* Detection Algorithms" by Ross Williams                        */
+/* (ross () guest adelaide edu au.). This document is likely to be  */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
+/*                                                               */
+/*****************************************************************/
+const unsigned long crc32c_table[256] = {
+        0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL,
+        0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL,
+        0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L,
+        0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+        0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L,
+        0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL,
+        0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L,
+        0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+        0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL,
+        0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L,
+        0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L,
+        0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+        0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL,
+        0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L,
+        0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L,
+        0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+        0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL,
+        0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL,
+        0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L,
+        0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+        0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL,
+        0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L,
+        0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL,
+        0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+        0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL,
+        0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL,
+        0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L,
+        0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+        0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L,
+        0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL,
+        0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL,
+        0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+        0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L,
+        0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL,
+        0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L,
+        0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+        0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L,
+        0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL,
+        0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L,
+        0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+        0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L,
+        0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L,
+        0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L,
+        0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+        0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL,
+        0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L,
+        0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L,
+        0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+        0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL,
+        0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L,
+        0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L,
+        0xAD7D5351L
+};
+
+/*
+ * Byte swap fix contributed by Dave Wysochanski <davidw () netapp com>.
+ */
+#define CRC32C_SWAP(crc32c_value)				\
+    (((crc32c_value & 0xff000000) >> 24)	|	\
+     ((crc32c_value & 0x00ff0000) >>  8)	|	\
+     ((crc32c_value & 0x0000ff00) <<  8)	|	\
+     ((crc32c_value & 0x000000ff) << 24))
+
+#define CRC32C(c,d) (c = (c >> 8) ^ crc32c_table[(c ^ (d)) & 0xFF])
+#define CRC32C_PRELOAD 0xffffffff
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _CRC32_H_ */
diff --git a/src/sfutil/sfportobject.c b/src/sfutil/sfportobject.c
index ea6655a..bd457e5 100644
--- a/src/sfutil/sfportobject.c
+++ b/src/sfutil/sfportobject.c
@@ -91,9 +91,9 @@
    PortVar - has been added. These are always added to the PortVarTable.
 
    --- Loading Port lists and rules
-   PortTables - we support src and dst tables for tcp/udp/icmp/ip/arp rules.
+   PortTables - we support src and dst tables for tcp/udp/sctp/icmp/ip/arp rules.
    PortVar References - we dup the PortVar entries as needed into each table if referenced,
-   so HTTP_PORTS for tcp and udp contain different rules.  If a rule references a PortVar
+   so HTTP_PORTS for tcp, udp, & sctp contain different rules.  If a rule references a PortVar
    we look it up in the table, if its not present we dup it from the PortVarTable, otherwise
    we just add the rule index to the PortVar HTTP_PORTS in the proper table. If a PortVar
    is not used to specify a Port entry in a rule we create a temp port-object, and check if
diff --git a/src/sfutil/sfportobject.h b/src/sfutil/sfportobject.h
index 0679dfb..294a470 100644
--- a/src/sfutil/sfportobject.h
+++ b/src/sfutil/sfportobject.h
@@ -165,16 +165,19 @@ typedef struct {
 
     PortTable * tcp_src, * tcp_dst;
     PortTable * udp_src, * udp_dst;
+    PortTable * sctp_src, * sctp_dst;
     PortTable * icmp_src,* icmp_dst;
     PortTable * ip_src,  * ip_dst;
     
     PortObject * tcp_anyany;
     PortObject * udp_anyany;
+    PortObject * sctp_anyany;
     PortObject * icmp_anyany;
     PortObject * ip_anyany;
     
     PortObject * tcp_nocontent; 
     PortObject * udp_nocontent; 
+    PortObject * sctp_nocontent;
     PortObject * icmp_nocontent; 
     PortObject * ip_nocontent; 
 
diff --git a/src/snort.c b/src/snort.c
index e3eb77b..825eb06 100644
--- a/src/snort.c
+++ b/src/snort.c
@@ -1721,7 +1721,7 @@ static int ShowUsage(char *program_name)
     FPUTS_BOTH ("        -H         Make hash tables deterministic.\n");
     FPUTS_BOTH ("        -i <if>    Listen on interface <if>\n");
     FPUTS_BOTH ("        -I         Add Interface name to alert output\n");
-    FPUTS_BOTH ("        -k <mode>  Checksum mode (all,noip,notcp,noudp,noicmp,none)\n");
+    FPUTS_BOTH ("        -k <mode>  Checksum mode (all,noip,notcp,noudp,nosctp,noicmp,none)\n");
     FPUTS_BOTH ("        -K <mode>  Logging mode (pcap[default],ascii,none)\n");
     FPUTS_BOTH ("        -l <ld>    Log to directory <ld>\n");
     FPUTS_BOTH ("        -L <file>  Log to this tcpdump file\n");
diff --git a/src/snort.h b/src/snort.h
index 0fbddec..cf58039 100644
--- a/src/snort.h
+++ b/src/snort.h
@@ -120,7 +120,8 @@
 #define DO_IP_CHECKSUMS     0x00000001
 #define DO_TCP_CHECKSUMS    0x00000002
 #define DO_UDP_CHECKSUMS    0x00000004
-#define DO_ICMP_CHECKSUMS   0x00000008
+#define DO_SCTP_CHECKSUMS   0x00000008
+#define DO_ICMP_CHECKSUMS   0x00000010
 
 #define LOG_UNIFIED         0x00000001
 #define LOG_TCPDUMP         0x00000002
@@ -499,7 +500,8 @@ typedef enum _ChecksumFlag
     CHECKSUM_FLAG__IP   = 0x00000001,
     CHECKSUM_FLAG__TCP  = 0x00000002,
     CHECKSUM_FLAG__UDP  = 0x00000004,
-    CHECKSUM_FLAG__ICMP = 0x00000008,
+    CHECKSUM_FLAG__SCTP = 0x00000008,
+    CHECKSUM_FLAG__ICMP = 0x00000010,
     CHECKSUM_FLAG__ALL  = 0x7fffffff
 
 } ChecksumFlag;
@@ -832,7 +834,7 @@ typedef struct _SnortConfig
 #endif
 
     /* The port-rule-maps map the src-dst ports to rules for
-     * udp and tcp, for Ip we map the dst port as the protocol,
+     * udp, tcp, & sctp; for Ip we map the dst port as the protocol,
      * and for Icmp we map the dst port to the Icmp type. This
      * allows us to use the decode packet information to in O(1)
      * select a group of rules to apply to the packet.  These
@@ -843,6 +845,7 @@ typedef struct _SnortConfig
     PORT_RULE_MAP *prmIpRTNX;
     PORT_RULE_MAP *prmTcpRTNX;
     PORT_RULE_MAP *prmUdpRTNX;
+    PORT_RULE_MAP *prmSctpRTNX;
     PORT_RULE_MAP *prmIcmpRTNX;
 
 #ifdef TARGET_BASED
@@ -894,6 +897,7 @@ typedef struct _PacketCount
     uint64_t other;
     uint64_t tcp;
     uint64_t udp;
+    uint64_t sctp;
     uint64_t icmp;
     uint64_t arp;
 #ifndef NO_NON_ETHER_DECODER
@@ -908,8 +912,10 @@ typedef struct _PacketCount
     uint64_t icmp6;
     uint64_t tdisc;
     uint64_t udisc;
+    uint64_t sctpdisc;
     uint64_t tcp6;
     uint64_t udp6;
+    uint64_t sctp6;
     uint64_t teredo;
     uint64_t ipdisc;
     uint64_t icmpdisc;
@@ -1252,6 +1258,16 @@ static inline int ScIcmpChecksumDrops(void)
     return snort_conf->targeted_policies[getRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__ICMP;
 }
 
+static inline int ScSctpChecksums(void)
+{
+    return snort_conf->targeted_policies[getDefaultPolicy()]->checksum_flags & CHECKSUM_FLAG__SCTP;
+}
+
+static inline int ScSctpChecksumDrops(void)
+{
+    return snort_conf->targeted_policies[getRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__SCTP;
+}
+
 static inline int ScIgnoreTcpPort(uint16_t port)
 {
     return snort_conf->ignore_ports[port] & PROTO_BIT__TCP;
@@ -1262,6 +1278,11 @@ static inline int ScIgnoreUdpPort(uint16_t port)
     return snort_conf->ignore_ports[port] & PROTO_BIT__UDP;
 }
 
+static inline int ScIgnoreSctpPort(uint16_t port)
+{
+    return snort_conf->ignore_ports[port] & PROTO_BIT__SCTP;
+}
+
 #ifdef MPLS
 static inline long int ScMplsStackDepth(void)
 {
diff --git a/src/target-based/sftarget_protocol_reference.c b/src/target-based/sftarget_protocol_reference.c
index ac35314..7b986b8 100644
--- a/src/target-based/sftarget_protocol_reference.c
+++ b/src/target-based/sftarget_protocol_reference.c
@@ -42,6 +42,7 @@
 
 int16_t protocolReferenceTCP;
 int16_t protocolReferenceUDP;
+int16_t protocolReferenceSCTP;
 int16_t protocolReferenceICMP;
 
 static SFGHASH *proto_reference_table = NULL;
@@ -53,6 +54,7 @@ static char *standard_protocols[] =
     "ip",
     "tcp",
     "udp",
+    "sctp",
     "icmp",
     /* Application Protocols */
     "http",
@@ -175,6 +177,7 @@ void InitializeProtocolReferenceTable(void)
     }
     protocolReferenceTCP = FindProtocolReference("tcp");
     protocolReferenceUDP = FindProtocolReference("udp");
+    protocolReferenceSCTP = FindProtocolReference("sctp");
     protocolReferenceICMP = FindProtocolReference("icmp");
 }
 
@@ -226,6 +229,9 @@ int16_t GetProtocolReference(Packet *p)
         case IPPROTO_UDP:
             ipprotocol = protocolReferenceUDP;
             break;
+        case IPPROTO_SCTP:
+            ipprotocol = protocolReferenceSCTP;
+            break;
         case IPPROTO_ICMP:
             ipprotocol = protocolReferenceICMP;
             break;
diff --git a/src/target-based/sftarget_protocol_reference.h b/src/target-based/sftarget_protocol_reference.h
index a2979b7..b7b96d4 100644
--- a/src/target-based/sftarget_protocol_reference.h
+++ b/src/target-based/sftarget_protocol_reference.h
@@ -38,6 +38,7 @@ typedef struct _SFTargetProtocolReference
 
 extern int16_t protocolReferenceTCP;
 extern int16_t protocolReferenceUDP;
+extern int16_t protocolReferenceSCTP;
 extern int16_t protocolReferenceICMP;
 
 void InitializeProtocolReferenceTable(void);
diff --git a/src/util.c b/src/util.c
index 7b6d466..c38711f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1081,6 +1081,7 @@ void DropStats(int exiting)
     LogStat("ICMP", pc.icmp, total);
     LogStat("UDP", pc.udp, total);
     LogStat("TCP", pc.tcp, total);
+    LogStat("SCTP", pc.sctp, total);
 
     LogStat("IP6", pc.ipv6, total);
     LogStat("IP6 Ext", pc.ip6ext, total);
@@ -1089,6 +1090,7 @@ void DropStats(int exiting)
     LogStat("ICMP6", pc.icmp6, total);
     LogStat("UDP6", pc.udp6, total);
     LogStat("TCP6", pc.tcp6, total);
+    LogStat("SCTP6", pc.sctp6, total);
     LogStat("Teredo", pc.teredo, total);
 
     LogStat("ICMP-IP", pc.embdip, total);
@@ -1125,6 +1127,7 @@ void DropStats(int exiting)
     LogStat("TCP Disc", pc.tdisc, total);
     LogStat("UDP Disc", pc.udisc, total);
     LogStat("ICMP Disc", pc.icmpdisc, total);
+    LogStat("SCTP Disc", pc.sctpdisc, total);
     LogStat("All Discard", pc.discards, total);
 
     LogStat("Other", pc.other, total);
@@ -1841,6 +1844,120 @@ void * SnortAlloc2(size_t size, const char *format, ...)
 }
 
 /**
+ * SnortStrToNumRng - Parses a number (signed) range split by a delimiter.
+ * @str: char pointer to the string to be parsed.
+ * @delimiter: char pointer to the delimiter separating the two ranges.
+ * @rng: int pointer to the smaller value of the range.
+ *
+ * Returns:
+ *   SNORT_STR2RNG_SUCCESS is a range was successfully parsed.
+ *   SNORT_STR2RNG_NOTRNG if the range delimiter was not found.
+ *   SNORT_STR2RNG_ERROR if an error condition was encountered.
+ */
+int SnortStrToNumRng(char *str, const char *delimiter, NumericRange *rng)
+{
+    if (!str || !delimiter || !rng)
+        return SNORT_STR2RNG_ERROR;
+
+    if (SnortStrcasestr(str, strlen(str), delimiter))
+    {
+        char *tok, *end;
+        int num = 0;
+
+        /* Get the first token. */
+        tok = strtok(str, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtol(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->min = num;
+
+        /* Get the second token. */
+        tok = strtok(NULL, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtol(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->max = num;
+
+        /* See if min is > max.  If so, swap them. */
+        if (rng->min > rng->max)
+        {
+            int tmp = rng->min;
+            rng->min = rng->max;
+            rng->max = tmp;
+        }
+
+        return SNORT_STR2RNG_SUCCESS;
+    } else {
+        return SNORT_STR2RNG_NOTRNG;
+    }
+}
+
+/**
+ * SnortStrToUNumRng - Parses a number (signed) range split by a delimiter.
+ * @str: char pointer to the string to be parsed.
+ * @delimiter: char pointer to the delimiter separating the two ranges.
+ * @rng: int pointer to the smaller value of the range.
+ *
+ * Returns:
+ *   SNORT_STR2RNG_SUCCESS is a range was successfully parsed.
+ *   SNORT_STR2RNG_NOTRNG if the range delimiter was not found.
+ *   SNORT_STR2RNG_ERROR if an error condition was encountered.
+ */
+int SnortStrToUNumRng(char *str, const char *delimiter, UNumericRange *rng)
+{
+    if (!str || !delimiter || !rng)
+        return SNORT_STR2RNG_ERROR;
+
+    if (SnortStrcasestr(str, strlen(str), delimiter))
+    {
+        char *tok, *end;
+        uint32_t num = 0;
+
+        /* Get the first token. */
+        tok = strtok(str, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtoul(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->min = num;
+
+        /* Get the second token. */
+        tok = strtok(NULL, delimiter);
+        if (!tok)
+            return SNORT_STR2RNG_ERROR;
+
+        num = strtoul(tok, &end, 10);
+        if (*end)
+            return SNORT_STR2RNG_ERROR;
+
+        rng->max = num;
+
+        /* See if min is > max.  If so, swap them. */
+        if (rng->min > rng->max)
+        {
+            uint32_t tmp = rng->min;
+            rng->min = rng->max;
+            rng->max = tmp;
+        }
+
+        return SNORT_STR2RNG_SUCCESS;
+    } else {
+        return SNORT_STR2RNG_NOTRNG;
+    }
+}
+
+/**
  * Chroot and adjust the snort_conf->log_dir reference
  *
  * @param directory directory to chroot to
diff --git a/src/util.h b/src/util.h
index f49c606..be07567 100644
--- a/src/util.h
+++ b/src/util.h
@@ -74,6 +74,10 @@
 
 #define SNORT_STRNLEN_ERROR -1
 
+#define SNORT_STR2RNG_SUCCESS 0
+#define SNORT_STR2RNG_ERROR -1
+#define SNORT_STR2RNG_NOTRNG -2
+
 #define SECONDS_PER_DAY  86400  /* number of seconds in a day  */
 #define SECONDS_PER_HOUR  3600  /* number of seconds in a hour */
 #define SECONDS_PER_MIN     60     /* number of seconds in a minute */
@@ -172,6 +176,19 @@ typedef struct _IntervalStats
 
 } IntervalStats;
 
+/* Used by rule keywords that involve number ranges. */
+typedef struct _NumericRange
+{
+    int min;
+    int max;
+} NumericRange;
+
+typedef struct _UNumericRange
+{
+    uint32_t min;
+    uint32_t max;
+} UNumericRange;
+
 
 /* Public function prototypes *************************************************/
 int DisplayBanner(void);
@@ -226,6 +243,8 @@ const char *SnortStrnStr(const char *s, int slen, const char *searchstr);
 const char *SnortStrcasestr(const char *s, int slen, const char *substr);
 void *SnortAlloc(unsigned long);
 void *SnortAlloc2(size_t, const char *, ...);
+int SnortStrToNumRng(char *, const char *, NumericRange *);
+int SnortStrToUNumRng(char *, const char *, UNumericRange *);
 char *CurrentWorkingDir(void);
 char *GetAbsolutePath(char *dir);
 char *StripPrefixDir(char *prefix, char *dir);
