[Snort-devel] Snort Supports SCTP

Joshua Kinard kumba at ...2185...
Mon May 20 01:20:16 EDT 2013


On 05/16/2013 7:53 AM, Russ Combs wrote:
> It is on our radar, but there are no specific plans at this point.
> 
> On Wed, May 15, 2013 at 5:06 AM, marwane azzouzi
> <azzouzi.marwane at ...3292...> wrote:
>>
>> Hello,
>>
>> My question concerns the support of the SCTP protocol by Snort in a mobile
>> context (SIGTRAN).
>> I see that there is no preprocessor to decode the SCTP protocol such like
>> SIP or HTTP preprocessors...
>> Did the team intend to develop that feature?
>>
>> Any information ?
>>
>> Thx
>>
>> marwane

Try the attached.  I have a strange fascination with SCTP, so back in 2011,
I copied the Stream5 UDP code and made a very generic SCTP Stream5 module,
as well as duplicated all the code points where UDP was parsed to parse
SCTP.  I also added a DecodeSCTP function and various helpers to decode.c,
and other bits that I'm not going to enumerate here.  I just updated all the
code today to work with snort-2.9.4.6, and tested it on both IPv4 and
IPv6-based packet captures that I managed to hunt down off of Google.

BIG NOTE: This DOES NOT perform ANY reassembly whatsoever.  It behaves
EXACTLY like Stream5 UDP.  Once it sees two packets in opposite directions,
it treats anything thereafter as a "stream" (session?) until the timeout
limit specified.  This means this is what the preproc config settings are
like (NEW denotes new items):

      preprocessor stream5_global: \
          track_tcp yes,           \
          track_udp yes,           \
          track_icmp yes,          \
NEW       track_sctp yes,          \
          max_tcp 1048576,         \
          max_udp 1048576,         \
NEW       max_sctp 1048576,        \
          max_icmp 1048576

NEW   preprocessor stream5_sctp:   \
NEW       timeout 600

NEW   config classification: sctp,SCTP Packet,1


I also added five new rule keyword options to control what you can alert on.
 Inspiration comes from the GTP preprocessor a wee bit on a few of them.
ALL of these are discrete options, too.  Put them before any payload
keywords for maximum firepower.

sctp_num_chunks:
Checks for the number of chunks bundled into a single SCTP packet.  Format
is exactly the same as dsize and friends:
    sctp_num_chunks:<value w/ or w/o range>;
    sctp_num_chunks:42;
    sctp_num_chunks:>13;
    sctp_num_chunks:10<>20;


sctp_chunk_type:
Limits the rule to specific SCTP chunk types.  Chunk types should be
specified in UPPERCASE format (hey, it's what the RFC's do).  More than one
chunk type can be specified, up to the limit of 21 (the current number of
chunks defined by the various RFC's):
    sctp_chunk_field:<CHUNK_TYPE1>[,<CHUNK_TYPE2>,CHUNK_TYPE3>...];
    sctp_chunk_type:DATA;
    sctp_chunk_type:INIT,INIT-ACK,PAD;


sctp_chunk_field:
Checks a specific field in a given chunk.  This keyword DOES NOT require
'sctp_chunk_type', though since it contains duplicitive code, I should
probably have it rely on that keyword at some point in the future.  That
said, it's got a funky format right now, and I don't have a complete list of
the various chunk fields in mail-friendly format.  Check
SctpChunkFieldParse() in src/detection-plugins/sp_sctp_chunk_field.c for those:
    sctp_chunk_field:<CHUNK_TYPE>,<field_name>,<value w/ or w/o range>;
    sctp_chunk_field:DATA,tsn,42;
    sctp_chunk_field:FORWARD-TSN,new_tsn,>31337;
    sctp_chunk_field:INIT-ACK,num_out_streams,10<>20;


sctp_cause_code:
Check for a specific cause code ONLY in ABORT or ERROR chunks.  This keyword
requires that you specify 'sctp_chunk_type' beforehand and it must be either
'ERROR', 'ABORT', or both.  These are the only two chunk types that support
causes.  Currently, this only accepts a numeric value representing the
specific cause code.  There's a ton of those things, and I haven't gotten
around to creating unique string names for them just yet.  you can get the
current list here:
https://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml#sctp-parameters-24
:
    sctp_cause_code:<value>;
    sctp_chunk_type:ABORT; sctp_cause_code:12;


sctp_chunk_flags:
Checks for various flag bits set or unset in specific chunk types.  This
keyword requires that you specify 'sctp_chunk_type' beforehand and it can
ONLY be one of the following chunk types: DATA, ABORT, SHUTDOWN-COMPLETE, or
PKTDROP.  The basic syntax is exactly like TCP's 'flags', but each of the
four (currently) supported chunk types each define their own flag bit.  See
src/detection-plugins/sp_sctp_chunk_flags.h for the current list:
    sctp_chunk_flags:<op><flag_bits>,<optional mask>;
    sctp_chunk_type:DATA; sctp_chunk_flags:*I;
    sctp_chunk_type:ABORT; sctp_chunk_flags:!T;
    sctp_chunk_type:PKTDROP; sctp_chunk_flags:*TB;


Also, a bit on the second patch that adds compiler.h.  This is borrowed from
the Linux kernel and an earlier version of this SCTP code used it to gain
some minor speed pickups in the decoder and rule options.  It defines the
likely() and unlikely() macros, which are gcc-specific, but they allow you
to "instruct" the compiler on the likely (or unlikely) conditionals.  I.e.,
everyone does a typical NULL check, if (foo->bar != NULL), but if you expect
a majority of cases will ALWAYS evaluate to true, wrap it in likely() and
gcc will save a few instructions on the assumption that foo->bar is never
NULL.  In the odd case where it is, well, you'll probably SIGSEGV anyways,
so what's the harm?  PS, never try to outsmart the compiler.  Just in some
cases, you can guide it a bit.

Snort devs should probably consider compiler.h and see what spots in the
decoder can make use of it for some small speed ups.  Well, for general
gcc-based builds that is.  With other compilers, these two macros are done
away with.

PS, yes, my copyright dates are all off.  No, I don't care.  If this were to
ever be accepted anyways, they'd all get changed.  Though, I DID borrow code
from WireShark (src/sfutil/adler32.{c|h} and crc32.h), and obviously,
compiler.h and BIT() from bitops.h in the Linux kernel.  So, however that
works.  Legal stuff, blah.

Sample rules (yes, these can use ports, too, I just wrote these up in a hurry):
alert sctp any any -> any any (msg:"SCTP DECODE TEST"; flow:established;
sctp_chunk_type:DATA; sid:42000132; rev:1; priority:1; classtype:sctp; )
alert sctp any any -> any any (msg:"SCTP DECODE TEST"; flow:established;
sctp_chunk_field:SACK,cuml_tsn_ack,>3000; sid:42000132; rev:1; priority:1;
classtype:sctp; )
alert sctp any any -> any any (msg:"SCTP DECODE TEST"; flow:established;
sctp_chunk_type:ABORT,ERROR; sctp_cause_code:12; sid:42000132; rev:1;
priority:1; classtype:sctp; )

Have fun!  I'll eventually update the patch to add the rule keywords to the
manual tex file.  Would definitely love some feedback.  Use my Gentoo
address for that.  Thanks!


-- 
Joshua Kinard
Gentoo/MIPS
kumba at ...2185...
4096R/D25D95E3 2011-03-28

"The past tempts us, the present confuses us, the future frightens us.  And
our lives slip away, moment by moment, lost in that vast, terrible in-between."

--Emperor Turhan, Centauri Republic
-------------- next part --------------
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 at ...3228... = sp_react.$(OBJEXT)
 @BUILD_RESPOND3_TRUE at ...3229... = 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. at ...3230...@ -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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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 at ...2499...>
+**
+** 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. at ...3230...@ -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 at ...402...>
 **
+** SCTP bits Copyright (C) 2013
+** Joshua Kinard <kumba12345 at ...2499...>
+**
 ** 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 at ...402...>
 ** Marc Norton <mnorton at ...402...>
 **
+** SCTP bits Copyright (C) 2013
+** Joshua Kinard <kumba12345 at ...2499...>
+**
 ** 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 at ...402...>
 ** Dan Roelker <droelker at ...402...>
+** Joshua Kinard <kumba12345 at ...2499...>
+**
 **
 ** 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 at ...402...>
 ** Dan Roelker <droelker at ...402...>
+** Joshua Kinard <kumba12345 at ...2499...>
 **
 **
 ** 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 at ...3228... = 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. at ...3230...@ -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 at ...3403...>
+ * 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 at ...3403...>
+ * 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 at ...3403...>
+ * 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 at ...94...). 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 at ...3404...>.
+ */
+#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);
-------------- next part --------------
diff --git a/src/compiler.h b/src/compiler.h
new file mode 100644
index 0000000..768e653
--- /dev/null
+++ b/src/compiler.h
@@ -0,0 +1,43 @@
+/*
+** Copyright (C) 2011 Joshua Kinard <kumba12345 at ...2499...>
+**
+** 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 __COMPILER_H__
+#define __COMPILER_H__
+
+/* Borrowed from include/linux/compiler.h */
+/*
+ * If using gcc, these macros help with branch prediction by allowing one
+ * to specify the likely branch that a conditional will take.  It's
+ * expensive if the branch is wrong, however, but can allow a coder with
+ * better knowledge than the compiler to optimize the code paths.  Use
+ * very sparingly, and only when you know with a reasonable certainty
+ * that you can outsmart gcc.
+ *
+ * If non-GNU, these macros do nothing.
+ */
+#ifdef __GNUC__
+  #define likely(x)      __builtin_expect(!!(x), 1)
+  #define unlikely(x)    __builtin_expect(!!(x), 0)
+#else
+  #define likely(x)      x
+  #define unlikely(x)    x
+ #endif
+
+#endif /* __COMPILER_H__ */
diff --git a/src/decode.c b/src/decode.c
index 762c93a..8aac65b 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -42,6 +42,7 @@
 #include "snort_debug.h"
 #include "util.h"
 #include "detect.h"
+#include "compiler.h"
 #include "checksum.h"
 #include "log.h"
 #include "generators.h"
@@ -3091,7 +3092,7 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
     uint16_t clen = ntohs(sctp_chunk->length);
 
     /* Check for zero-length chunk (minimum should ALWAYs be 4 bytes. */
-    if (clen == 0)
+    if (unlikely(clen == 0))
     {
         SctpChunkAnomaly(p, clen, "SCTP Chunk Length is 0 bytes",
                          DECODE_SCTP_CHUNK_ZERO_LENGTH,
@@ -3118,7 +3119,7 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
              * well any flags on the other chunks.  Not for here, of course,
              * but for future SCTP rule options.
              */
-            if ((cflags & 0xf0) > 0)
+            if (unlikely(cflags & 0xf0) > 0)
             {
                 SctpChunkAnomaly(p, clen, "SCTP DATA chunk flags reserved field "
                                  "is non-zero", DECODE_SCTP_DATA_C_RSVD_NZERO,
@@ -3140,28 +3141,28 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
              *   - 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)
+            if (unlikely(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)
+            else if (unlikely(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)
+            else if (unlikely(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)
+            else if (unlikely(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,
@@ -3182,21 +3183,21 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
              *   - 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)
+            if (unlikely(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)
+            else if (unlikely(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)
+            else if (unlikely(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,
@@ -3235,7 +3236,7 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
             CHECK_CHUNK_LENGTH(ABORT, SCTP_ABORT_C_LEN)
 
             /* Make sure the ABORT chunk's reserved area in flags is 0. */
-            if ((cflags & 0xfe) > 0)
+            if (unlikely(cflags & 0xfe) > 0)
             {
                 SctpChunkAnomaly(p, clen, "SCTP ABORT chunk flags reserved field "
                                  "is non-zero", DECODE_SCTP_ABORT_C_RSVD_NZERO,
@@ -3311,7 +3312,7 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
             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)
+            if (unlikely(cflags & 0xfe) > 0)
             {
                 SctpChunkAnomaly(p, clen, "SCTP SHUTDOWN_COMPLETE chunk flags reserved field "
                                  "is non-zero", DECODE_SCTP_SHUTDOWN_COMPLETE_C_RSVD_NZERO,
@@ -3342,14 +3343,14 @@ void SctpChunkTests(Packet *p, const SctpChunk *sctp_chunk)
             CHECK_CHUNK_LENGTH(PKTDROP, SCTP_PKTDROP_C_LEN)
 
             /* Make sure the PKTDROP chunk's reserved area in flags is 0. */
-            if ((cflags & 0xf0) > 0)
+            if (unlikely(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)
+            else if (unlikely(ntohs(sctp_chunk->pktdrop.reserved)) != 0)
             {
                 SctpChunkAnomaly(p, clen, "SCTP PKTDROP Chunk reserved field "
                                  "is non-zero", DECODE_SCTP_PKTDROP_C_RSVD_NZERO,
@@ -3499,7 +3500,7 @@ inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen)
  */
  void DecodeSCTP(const uint8_t * pkt, const uint32_t len, Packet * p)
  {
-    if (len < SCTP_COMMON_HEADER_LEN)
+    if(unlikely(len < SCTP_COMMON_HEADER_LEN))
     {
         DEBUG_WRAP(DebugMessage(DEBUG_DECODE,
             "Truncated SCTP common header (%d bytes)\n", len));
@@ -3539,7 +3540,7 @@ inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen)
          */
         uint32_t csum_crc32 = SctpCrc32Chksum((unsigned char *)p->sctph, len);
 
-        if ((csum_crc32 == 0) || (csum_crc32 != csum_pkt))
+        if (unlikely((csum_crc32 == 0) || (csum_crc32 != csum_pkt)))
         {
             /* Try Adler32. */
             uint32_t csum_adler32 = SctpAdler32Chksum((unsigned char *)p->sctph, len);
@@ -3562,7 +3563,7 @@ inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen)
             csum_calc = csum_crc32;
         }
 
-        if (!csum_valid)
+        if (unlikely(!csum_valid))
         {
             /*
              * Don't drop the packet if this is encapuslated in Teredo or ESP.
@@ -3618,7 +3619,7 @@ inline uint16_t SctpGetNextParam(SctpParam *p, uint16_t rclen, uint16_t plen)
      * decoder anomalies, based on RFC4960's guidelines. Chunk bundling is
      * explained in RFC4960, Section 6.10.
      */
-    while ((chunk_data != NULL) && (remaining_len != 0))
+    while (likely(chunk_data != NULL) && (remaining_len != 0))
     {
         SctpChunkTests(p, chunk_data);
         chunk_length = SCTP_ADD_PADDING(ntohs(chunk_data->length));
diff --git a/src/detection-plugins/sp_sctp_cause_code.c b/src/detection-plugins/sp_sctp_cause_code.c
index 9d7a5ed..985d388 100644
--- a/src/detection-plugins/sp_sctp_cause_code.c
+++ b/src/detection-plugins/sp_sctp_cause_code.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "snort_debug.h"
 #include "plugin_enum.h"
+#include "compiler.h"
 #include "sp_sctp_cause_code.h"
 #include "sp_sctp_chunk_type.h"
 
@@ -225,7 +226,7 @@ int SctpCauseCodeEval(void *data, Packet *p)
     int rval = DETECTION_OPTION_NO_MATCH;
     PROFILE_VARS;
 
-    if(!p->sctph || !sccd)
+    if(!p->sctph || unlikely(!sccd))
         return rval;
 
     PREPROC_PROFILE_START(sctpCauseCodePerfStats);
@@ -237,7 +238,7 @@ int SctpCauseCodeEval(void *data, Packet *p)
      * 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) && 
+    while (likely(c != NULL) && 
            (remaining_len != 0) && 
            (rval == DETECTION_OPTION_NO_MATCH))
     {
@@ -301,7 +302,7 @@ int SctpCauseCodeEval(void *data, Packet *p)
          * Note that ABORT can have zero or more causes and ERROR will have
          * at least one cause.
          */
-        while ((cause != NULL) &&
+        while (likely(cause != NULL) &&
                (remaining_chunk_len != 0) &&
                (rval == DETECTION_OPTION_NO_MATCH))
         {
diff --git a/src/detection-plugins/sp_sctp_chunk_field.c b/src/detection-plugins/sp_sctp_chunk_field.c
index bbc99cb..131b30e 100644
--- a/src/detection-plugins/sp_sctp_chunk_field.c
+++ b/src/detection-plugins/sp_sctp_chunk_field.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "snort_debug.h"
 #include "plugin_enum.h"
+#include "compiler.h"
 #include "sp_sctp_chunk_field.h"
 
 #include "snort.h"
@@ -435,7 +436,7 @@ int SctpChunkFieldEval(void *data, Packet *p)
     int rval = DETECTION_OPTION_NO_MATCH;
     PROFILE_VARS;
 
-    if(!p->sctph || !scfd)
+    if(!p->sctph || unlikely(!scfd))
         return rval;
 
     PREPROC_PROFILE_START(sctpChunkFieldPerfStats);
@@ -450,7 +451,7 @@ int SctpChunkFieldEval(void *data, Packet *p)
      * 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) &&
+    while (likely(c != NULL) &&
            (remaining_len != 0) &&
            (rval == DETECTION_OPTION_NO_MATCH))
     {
diff --git a/src/detection-plugins/sp_sctp_chunk_flags.c b/src/detection-plugins/sp_sctp_chunk_flags.c
index 4e7f57f..d4f1425 100644
--- a/src/detection-plugins/sp_sctp_chunk_flags.c
+++ b/src/detection-plugins/sp_sctp_chunk_flags.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "snort_debug.h"
 #include "plugin_enum.h"
+#include "compiler.h"
 #include "sp_sctp_chunk_flags.h"
 #include "sp_sctp_chunk_type.h"
 
@@ -374,7 +375,7 @@ int SctpChunkFlagsEval(void *data, Packet *p)
     int rval = DETECTION_OPTION_NO_MATCH;
     PROFILE_VARS;
 
-    if(!p->sctph || !scfd)
+    if(!p->sctph || unlikely(!scfd))
         return rval;
 
     PREPROC_PROFILE_START(sctpChunkFlagsPerfStats);
@@ -386,7 +387,7 @@ int SctpChunkFlagsEval(void *data, Packet *p)
      * 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) &&
+    while (likely(c != NULL) &&
            (remaining_len != 0) &&
            (rval == DETECTION_OPTION_NO_MATCH))
     {
diff --git a/src/detection-plugins/sp_sctp_chunk_type.c b/src/detection-plugins/sp_sctp_chunk_type.c
index ad3b4bc..b3fa4fd 100644
--- a/src/detection-plugins/sp_sctp_chunk_type.c
+++ b/src/detection-plugins/sp_sctp_chunk_type.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "snort_debug.h"
 #include "plugin_enum.h"
+#include "compiler.h"
 #include "sp_sctp_chunk_type.h"
 
 #include "snort.h"
@@ -298,7 +299,7 @@ int SctpChunkTypeEval(void *data, Packet *p)
     int rval;
     PROFILE_VARS;
 
-    if(!p->sctph || !sctd)
+    if(!p->sctph || unlikely(!sctd))
         return DETECTION_OPTION_NO_MATCH;
 
     PREPROC_PROFILE_START(sctpChunkTypePerfStats);
@@ -319,7 +320,7 @@ int SctpChunkTypeEval(void *data, Packet *p)
      * member in the SctpChunkTypeData struct.  If it is true, then see
      * whether we have to deal with negation.
      */
-    while ((c != NULL) && (remaining_len != 0))
+    while (likely(c != NULL) && (remaining_len != 0))
     {
         uint16_t chunk_length = SCTP_ADD_PADDING(ntohs(c->length));
         remaining_len -= chunk_length;
diff --git a/src/detection-plugins/sp_sctp_num_chunks.c b/src/detection-plugins/sp_sctp_num_chunks.c
index 69d8ef5..54e0bae 100644
--- a/src/detection-plugins/sp_sctp_num_chunks.c
+++ b/src/detection-plugins/sp_sctp_num_chunks.c
@@ -36,6 +36,7 @@
 #include "util.h"
 #include "snort_debug.h"
 #include "plugin_enum.h"
+#include "compiler.h"
 #include "sp_sctp_num_chunks.h"
 
 #include "snort.h"
@@ -245,7 +246,7 @@ int SctpNumChunksEval(void *data, Packet *p)
     int rval = DETECTION_OPTION_NO_MATCH;
     PROFILE_VARS;
 
-    if(!p->sctph || !sncd)
+    if(!p->sctph || unlikely(!sncd))
         return rval;
 
     PREPROC_PROFILE_START(sctpNumChunksPerfStats);
diff --git a/src/preprocessors/Stream5/snort_stream5_sctp.c b/src/preprocessors/Stream5/snort_stream5_sctp.c
index 70f76b3..a09a60c 100644
--- a/src/preprocessors/Stream5/snort_stream5_sctp.c
+++ b/src/preprocessors/Stream5/snort_stream5_sctp.c
@@ -31,6 +31,7 @@
 #include "sfxhash.h"
 #include "util.h"
 #include "decode.h"
+#include "compiler.h"
 
 #include "stream5_common.h"
 #include "stream_api.h"
@@ -90,7 +91,7 @@ static int ProcessSctp(Stream5LWSession *, Packet *, Stream5SctpPolicy *, SFXHAS
 
 void Stream5InitSctp(Stream5GlobalConfig *gconfig)
 {
-    if (gconfig == NULL)
+    if (unlikely(gconfig == NULL))
         return;
 
     /* Now SCTP */
@@ -99,14 +100,14 @@ void Stream5InitSctp(Stream5GlobalConfig *gconfig)
         sctp_lws_cache = InitLWSessionCache(gconfig->max_sctp_sessions,
                                            30, (3*60), 5, 0, &SctpSessionCleanup);
 
-        if(!sctp_lws_cache)
+        if(unlikely(!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)
+        if (unlikely(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__);
@@ -118,7 +119,7 @@ void Stream5SctpPolicyInit(Stream5SctpConfig *config, char *args)
 {
     Stream5SctpPolicy *s5SctpPolicy;
 
-    if (config == NULL)
+    if (unlikely(config == NULL))
         return;
 
     s5SctpPolicy = (Stream5SctpPolicy *)SnortAlloc(sizeof(Stream5SctpPolicy));
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 834 bytes
Desc: OpenPGP digital signature
URL: <https://lists.snort.org/pipermail/snort-devel/attachments/20130520/9683fd88/attachment.sig>


More information about the Snort-devel mailing list